Skip to main content

ravenrs_app/model/
node.rs

1//! A node of the application model.
2
3use std::collections::BTreeMap;
4
5use serde_json::Value as Json;
6
7/// A node of the application model: an id, settings and nodes under it.
8#[derive(Clone, Debug, Default, PartialEq)]
9#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
10pub struct ModelNode {
11    /// What it is known by among the nodes beside it.
12    pub id: String,
13    /// Its settings.
14    pub properties: BTreeMap<String, Json>,
15    /// The nodes under it, in the order they came.
16    pub children: Vec<ModelNode>,
17}
18
19#[cfg_attr(feature = "live", ravenrs_live::expose)]
20impl ModelNode {
21    /// A node with no settings and nothing under it.
22    pub fn new(id: impl Into<String>) -> Self {
23        Self { id: id.into(), ..Self::default() }
24    }
25
26    /// With this setting.
27    pub fn with(mut self, name: &str, value: impl Into<Json>) -> Self {
28        self.properties.insert(name.to_owned(), value.into());
29        self
30    }
31
32    /// With this node under it.
33    pub fn child(mut self, child: ModelNode) -> Self {
34        self.children.push(child);
35        self
36    }
37
38    /// The nodes under it, as they are, including those an index hides.
39    pub(crate) fn in_order_all(&self) -> &[ModelNode] {
40        &self.children
41    }
42
43    /// The node under it with this id.
44    pub fn get(&self, id: &str) -> Option<&ModelNode> {
45        self.children.iter().find(|c| c.id == id)
46    }
47
48    pub(crate) fn get_mut(&mut self, id: &str) -> Option<&mut ModelNode> {
49        self.children.iter_mut().find(|c| c.id == id)
50    }
51
52    pub(crate) fn at_mut(&mut self, path: &[&str]) -> Option<&mut ModelNode> {
53        path.iter().try_fold(self, |node, id| node.get_mut(id))
54    }
55
56    /// The node at this path of ids below it.
57    pub fn at(&self, path: &[&str]) -> Option<&ModelNode> {
58        path.iter().try_fold(self, |node, id| node.get(id))
59    }
60
61    /// A setting as text.
62    pub fn text(&self, name: &str) -> Option<&str> {
63        self.properties.get(name).and_then(Json::as_str)
64    }
65
66    /// A setting as it is typed: text as it is, and anything else as JSON.
67    pub fn typed(&self, name: &str) -> Option<String> {
68        self.properties.get(name).map(|value| match value {
69            Json::String(text) => text.clone(),
70            other => other.to_string(),
71        })
72    }
73
74    /// A setting as a whole number.
75    pub fn integer(&self, name: &str) -> Option<i64> {
76        self.properties.get(name).and_then(Json::as_i64)
77    }
78
79    /// A setting as a number.
80    pub fn number(&self, name: &str) -> Option<f64> {
81        self.properties.get(name).and_then(Json::as_f64)
82    }
83
84    /// A setting as yes or no.
85    pub fn flag(&self, name: &str) -> Option<bool> {
86        self.properties.get(name).and_then(Json::as_bool)
87    }
88
89    /// The nodes under it in the order to show them: by `Index`, then as they came.
90    /// A negative index keeps a node out of sight, so it is not here.
91    pub fn in_order(&self) -> Vec<&ModelNode> {
92        let mut shown: Vec<&ModelNode> =
93            self.children.iter().filter(|c| c.integer("Index").is_none_or(|i| i >= 0)).collect();
94        shown.sort_by_key(|c| c.integer("Index").unwrap_or(i64::MAX));
95        shown
96    }
97}