biodivine_lib_param_bn/_impl_annotations/
_impl_annotation.rs

1use crate::_impl_annotations::validate_path_segment;
2use crate::ModelAnnotation;
3use std::collections::HashMap;
4use std::fmt::{Debug, Formatter};
5
6impl ModelAnnotation {
7    /// Create a new empty annotation.
8    pub fn new() -> ModelAnnotation {
9        ModelAnnotation {
10            value: Default::default(),
11            inner: Default::default(),
12        }
13    }
14
15    /// Create a new annotation with a given `value` and no children.
16    pub fn with_value(value: String) -> ModelAnnotation {
17        ModelAnnotation {
18            value: Some(value),
19            inner: Default::default(),
20        }
21    }
22
23    /// Get a reference to a child `ModelAnnotation` by specifying a path.
24    ///
25    /// If such annotation does not exist, returns `None`.
26    pub fn get_child<T: AsRef<str>>(&self, path: &[T]) -> Option<&ModelAnnotation> {
27        if path.is_empty() {
28            Some(self)
29        } else {
30            self.inner
31                .get(path[0].as_ref())
32                .and_then(|inner| inner.get_child(&path[1..]))
33        }
34    }
35
36    /// Get a reference to a mutable child `ModelAnnotation` by specifying a path.
37    ///
38    /// If such annotation dues not exist, returns `None`. If you want to instead create
39    /// the path when it does not exist, use `ModelAnnotation::ensure_child`.
40    pub fn get_mut_child<T: AsRef<str>>(&mut self, path: &[T]) -> Option<&mut ModelAnnotation> {
41        if path.is_empty() {
42            Some(self)
43        } else {
44            self.inner
45                .get_mut(path[0].as_ref())
46                .and_then(|inner| inner.get_mut_child(&path[1..]))
47        }
48    }
49
50    /// Get a mutable reference to a `ModelAnnotation` child, just as `get_mut_child`, but
51    /// if some part of the path does not exist, empty annotations are created to populate it.
52    ///
53    /// Panics: Fails if the child path segments contain curly brackets.
54    pub fn ensure_child<T: AsRef<str>>(&mut self, path: &[T]) -> &mut ModelAnnotation {
55        if path.is_empty() {
56            self
57        } else {
58            // Sadly, we have to run the `to_string` here, as we are not sure if the key exists.
59            validate_path_segment(path[0].as_ref());
60            let entry = self.inner.entry(path[0].as_ref().to_string()).or_default();
61            entry.ensure_child(&path[1..])
62        }
63    }
64
65    /// Ensure that the given value is present at the given path. If the value is not present,
66    /// it is inserted. If the value exists, but is different, it is overwritten. Returns `true`
67    /// if the value has been updated.
68    pub fn ensure_value<T: AsRef<str>>(&mut self, path: &[T], value: &str) -> bool {
69        let annotation = self.ensure_child(path);
70        if let Some(current) = annotation.value.as_ref() {
71            if current == value {
72                false
73            } else {
74                annotation.value = Some(value.to_string());
75                true
76            }
77        } else {
78            annotation.value = Some(value.to_string());
79            true
80        }
81    }
82
83    /// Append the given value to the value currently stored at the given path. If there is no
84    /// such value, a new value is created.
85    ///
86    /// Note that this function does not automatically add a newline to the given `value`.
87    /// If you want the value to have newlines, you must add them yourself.
88    pub fn append_value<T: AsRef<str>>(&mut self, path: &[T], value: &str) {
89        let annotation = self.ensure_child(path);
90        if let Some(current) = annotation.value.as_mut() {
91            current.push_str(value);
92        } else {
93            annotation.value = Some(value.to_string());
94        }
95    }
96
97    /// Retrieve an annotation value stored at the given path,
98    /// or `None` if such path/value does not exist.
99    pub fn get_value<T: AsRef<str>>(&self, path: &[T]) -> Option<&String> {
100        if path.is_empty() {
101            self.value.as_ref()
102        } else {
103            self.inner
104                .get(path[0].as_ref())
105                .and_then(|inner| inner.get_value(&path[1..]))
106        }
107    }
108
109    /// Get the annotation value for this tree node.
110    pub fn value(&self) -> Option<&String> {
111        self.value.as_ref()
112    }
113
114    /// Same as `value`, but mutable.
115    pub fn value_mut(&mut self) -> &mut Option<String> {
116        &mut self.value
117    }
118
119    /// Get the map of all child `ModelAnnotations`.
120    pub fn children(&self) -> &HashMap<String, ModelAnnotation> {
121        &self.inner
122    }
123
124    /// Self as `children` but mutable.
125    pub fn children_mut(&mut self) -> &mut HashMap<String, ModelAnnotation> {
126        &mut self.inner
127    }
128}
129
130impl Default for ModelAnnotation {
131    fn default() -> Self {
132        ModelAnnotation::new()
133    }
134}
135
136impl Debug for ModelAnnotation {
137    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
138        write!(f, "Annotation[{:?}][", self.value)?;
139        // Sorting ensures deterministic representation.
140        let mut keys = self.inner.keys().collect::<Vec<_>>();
141        keys.sort();
142        for k in keys {
143            write!(f, "(`{}` : {:?})", k, self.inner.get(k).unwrap())?;
144        }
145        write!(f, "]")
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use crate::ModelAnnotation;
152
153    #[test]
154    pub fn simple_annotation_test() {
155        let mut annotation = ModelAnnotation::default();
156
157        assert!(annotation.children().is_empty());
158        assert!(annotation.value().is_none());
159
160        // Return value of `ensure_value` is correct.
161        assert!(annotation.ensure_value::<&str>(&[], "Value 1"));
162        assert_eq!("Value 1", annotation.value().unwrap().as_str());
163        assert!(!annotation.ensure_value::<&str>(&[], "Value 1"));
164
165        // Value append works:
166        annotation.ensure_value::<&str>(&[], "Hello\n");
167        annotation.append_value::<&str>(&[], "World\n");
168        assert_eq!("Hello\nWorld\n", annotation.value().unwrap().as_str());
169
170        // We can also add values/children through the mut bindings:
171        annotation.value_mut().as_mut().unwrap().push_str("Value 3");
172        assert_eq!(
173            "Hello\nWorld\nValue 3",
174            annotation.value().unwrap().as_str()
175        );
176
177        annotation.children_mut().insert(
178            "Child 1".to_string(),
179            ModelAnnotation::with_value("Child value".to_string()),
180        );
181        assert!(annotation.get_child(&["Child 1"]).is_some());
182        assert_eq!(
183            "Child value",
184            annotation.get_value(&["Child 1"]).unwrap().as_str()
185        );
186
187        assert!(annotation.get_child(&["Child 1", "Child 2"]).is_none());
188        annotation.ensure_child(&["Child 1", "Child 2"]);
189        assert!(annotation.get_child(&["Child 1", "Child 2"]).is_some());
190    }
191
192    #[test]
193    #[should_panic]
194    pub fn add_invalid_annotation_child() {
195        // Backticks are not allowed.
196        let mut annotation = ModelAnnotation::default();
197        annotation.ensure_child(&["va`R`"]);
198    }
199
200    #[test]
201    #[should_panic]
202    pub fn print_invalid_annotation() {
203        let mut annotations = ModelAnnotation::default();
204        annotations.children_mut().insert(
205            "hello\nworld".to_string(),
206            ModelAnnotation::with_value("Value".to_string()),
207        );
208        annotations.to_string();
209    }
210}