setting.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. io::Cursor,
  20. sync::{Arc, Mutex as SyncMutex},
  21. };
  22. use darkfi_serial::{Decodable, Encodable, VarInt};
  23. use kvdb_overlay::Tree;
  24. use crate::{
  25. error::{Error, Result},
  26. prop::{
  27. Property, PropertyAtomicGuard, PropertyPtr, PropertySubType, PropertyType, PropertyValue,
  28. Role,
  29. },
  30. scene::{CallArgType, Pimpl, SceneNode, SceneNodeType, SceneNodeWeak},
  31. ExecutorPtr,
  32. };
  33. /// Settings when modified are persisted otherwise they use their default
  34. /// as expected by properties.
  35. ///
  36. /// Settings can be used directly by other nodes or as a dependency which
  37. /// can be used in expressions.
  38. ///
  39. /// Settings are set by the user with `Role::User`.
  40. ///
  41. /// For example `net.enable_tor` might be implicitly used by `/plugin/darkirc`
  42. /// while some other setting might be used in the schema itself with the node
  43. /// not being aware its depending on an external property.
  44. ///
  45. /// In both cases modifying the setting should propagate the changes to that node.
  46. ///
  47. /// Although the `/setting2` root has no knowledge of property paths underneath there
  48. /// is a convention of using `foo.bar.baz` to namespace the settings.
  49. pub fn create_setting(name: &str) -> SceneNode {
  50. let mut node = SceneNode::new(name, SceneNodeType::Setting);
  51. // Example
  52. let prop = Property::new("net.enable_tor", PropertyType::Bool, PropertySubType::Null);
  53. node.add_property(prop).unwrap();
  54. let mut prop = Property::new("win.scale", PropertyType::Float32, PropertySubType::Null);
  55. prop.set_defaults_f32(vec![1.]).unwrap();
  56. prop.set_range_f32(0., f32::MAX);
  57. node.add_property(prop).unwrap();
  58. node.add_method(
  59. "search",
  60. vec![("filter", "Filter string to search keys", CallArgType::Str)],
  61. None,
  62. )
  63. .unwrap();
  64. node
  65. }
  66. pub type SettingPtr = Arc<Setting>;
  67. pub struct Setting {
  68. tasks: SyncMutex<Vec<smol::Task<()>>>,
  69. }
  70. impl Setting {
  71. pub async fn new(node: SceneNodeWeak, db_tree: Tree, ex: ExecutorPtr) -> Pimpl {
  72. let node_ref = node.upgrade().unwrap();
  73. // Load any persisted properties from the db.
  74. for entry in db_tree.iter() {
  75. let (key, data) = entry.unwrap();
  76. let key = String::from_utf8(key).unwrap();
  77. let prop = node_ref.get_property(&key).unwrap();
  78. Self::load_prop(&prop, &data).unwrap();
  79. }
  80. // Spawn tasks persisting our properties to the db when they change.
  81. let mut tasks = vec![];
  82. for prop in &node_ref.props {
  83. let db_tree2 = db_tree.clone();
  84. let prop2 = prop.clone();
  85. let on_modify_sub = prop.subscribe_modify();
  86. let task = ex.spawn(async move {
  87. while let Ok((_role, _action, _guard)) = on_modify_sub.receive().await {
  88. Self::save_prop(&prop2, &db_tree2).unwrap();
  89. }
  90. });
  91. tasks.push(task);
  92. }
  93. Pimpl::Setting(Arc::new(Self { tasks: SyncMutex::new(tasks) }))
  94. }
  95. /// Persist the state of a property under its name as the db key.
  96. /// The db value is a list of (prop_idx, prop_value) pairs exactly
  97. /// specifying which idxs inside the property are set. If none are
  98. /// set the key is dropped.
  99. fn save_prop(prop: &PropertyPtr, db_tree: &Tree) -> Result<()> {
  100. assert!(prop.is_bounded());
  101. let mut data = vec![];
  102. let mut is_set = false;
  103. for i in 0..prop.get_len() {
  104. if prop.is_unset(i)? {
  105. continue
  106. }
  107. is_set = true;
  108. let val = prop.get_value(i)?;
  109. VarInt(i as u64).encode(&mut data)?;
  110. Self::encode_value(prop, &val, &mut data)?;
  111. }
  112. if is_set {
  113. db_tree.insert(prop.name.as_bytes(), &data)?;
  114. } else {
  115. db_tree.remove(prop.name.as_bytes())?;
  116. }
  117. Ok(())
  118. }
  119. /// Serialize a single value. The property type determines the binary
  120. /// format of the value itself. If the property allows null values then
  121. /// it is written as an option (`Option<X>`): a single tag byte followed
  122. /// by the value only when it is not null.
  123. fn encode_value(prop: &Property, val: &PropertyValue, data: &mut Vec<u8>) -> Result<()> {
  124. if prop.is_null_allowed {
  125. match val {
  126. PropertyValue::Null => {
  127. false.encode(data)?;
  128. }
  129. val => {
  130. true.encode(data)?;
  131. val.encode(data)?;
  132. }
  133. }
  134. } else {
  135. val.encode(data)?;
  136. }
  137. Ok(())
  138. }
  139. /// Restore a property state previously written by `Self::save_prop()`.
  140. fn load_prop(prop: &PropertyPtr, data: &[u8]) -> Result<()> {
  141. assert!(prop.is_bounded());
  142. let mut cur = Cursor::new(data);
  143. let atom = &mut PropertyAtomicGuard::none();
  144. while (cur.position() as usize) < data.len() {
  145. let i = VarInt::decode(&mut cur)?.0 as usize;
  146. let val = Self::decode_value(prop, &mut cur)?;
  147. Self::apply_value(prop, atom, i, val)?;
  148. }
  149. Ok(())
  150. }
  151. /// Decode a value serialized by `Self::encode_value()`.
  152. fn decode_value(prop: &Property, cur: &mut Cursor<&[u8]>) -> Result<PropertyValue> {
  153. macro_rules! decode_ty {
  154. ($typ:ty, $variant:ident) => {{
  155. if prop.is_null_allowed {
  156. match Option::<$typ>::decode(cur)? {
  157. Some(v) => PropertyValue::$variant(v),
  158. None => PropertyValue::Null,
  159. }
  160. } else {
  161. PropertyValue::$variant(<$typ>::decode(cur)?)
  162. }
  163. }};
  164. }
  165. let val = match prop.typ {
  166. PropertyType::Bool => decode_ty!(bool, Bool),
  167. PropertyType::Uint32 => decode_ty!(u32, Uint32),
  168. PropertyType::Float32 => decode_ty!(f32, Float32),
  169. PropertyType::Str => decode_ty!(String, Str),
  170. PropertyType::Enum => decode_ty!(String, Enum),
  171. PropertyType::SceneNodeId => decode_ty!(u32, SceneNodeId),
  172. PropertyType::Null | PropertyType::SExpr => return Err(Error::PropertyWrongType),
  173. };
  174. Ok(val)
  175. }
  176. /// Set the value at index `i` as `Role::User`.
  177. fn apply_value(
  178. prop: &PropertyPtr,
  179. atom: &mut PropertyAtomicGuard,
  180. i: usize,
  181. val: PropertyValue,
  182. ) -> Result<()> {
  183. match val {
  184. PropertyValue::Bool(v) => prop.set_bool(atom, Role::User, i, v),
  185. PropertyValue::Uint32(v) => prop.set_u32(atom, Role::User, i, v),
  186. PropertyValue::Float32(v) => prop.set_f32(atom, Role::User, i, v),
  187. PropertyValue::Str(v) => prop.set_str(atom, Role::User, i, v),
  188. PropertyValue::Enum(v) => prop.set_enum(atom, Role::User, i, v),
  189. PropertyValue::SceneNodeId(v) => prop.set_node_id(atom, Role::User, i, v),
  190. PropertyValue::Null => prop.set_null(atom, Role::User, i),
  191. PropertyValue::Unset | PropertyValue::SExpr(_) => Err(Error::PropertyWrongType),
  192. }
  193. }
  194. }
  195. impl Drop for Setting {
  196. fn drop(&mut self) {
  197. self.tasks.lock().unwrap().clear();
  198. }
  199. }