reactive_graph_graph/instances/properties/
property_instance.rs

1use std::collections::BTreeMap;
2use std::hash::Hash;
3use std::hash::Hasher;
4use std::ops::Deref;
5use std::ops::DerefMut;
6
7use dashmap::DashMap;
8use dashmap::DashSet;
9use schemars::JsonSchema;
10use schemars::Schema;
11use schemars::SchemaGenerator;
12use schemars::json_schema;
13use serde::Deserialize;
14use serde::Serialize;
15use serde_json::Map;
16use serde_json::Value;
17use std::borrow::Cow;
18
19use crate::HashableValue;
20use crate::MutablePropertyInstanceSetter;
21use crate::PropertyInstanceGetter;
22use crate::PropertyTypes;
23
24pub struct ContainerPropertyInstance<IdType: Clone> {
25    /// Id of the container
26    pub id: IdType,
27
28    /// Property name
29    pub name: String,
30
31    /// Store the current value
32    pub value: Value,
33}
34
35impl<IdType: Clone> ContainerPropertyInstance<IdType> {
36    pub fn new<N: Into<String>>(id: IdType, name: N, value: Value) -> Self {
37        ContainerPropertyInstance { id, name: name.into(), value }
38    }
39}
40
41pub type PropertyNames = DashSet<String>;
42
43/// Container for property instances.
44/// PropertyInstances exposes all functionality from the underlying DashMap<String, Value>.
45#[derive(Clone, Debug, Default, Serialize, Deserialize)]
46pub struct PropertyInstances(DashMap<String, Value>);
47
48impl PropertyInstances {
49    /// Creates an empty map of property instances.
50    pub fn new() -> Self {
51        PropertyInstances(DashMap::new())
52    }
53
54    /// Creates property instances from a property type and initializes the values with the
55    /// default values of the data type of the property.
56    pub fn new_from_property_types_with_defaults(property_types: &PropertyTypes) -> Self {
57        let properties = Self::new();
58        for property_type in property_types.iter() {
59            properties.insert(property_type.key().clone(), property_type.data_type.default_value());
60        }
61        properties
62    }
63
64    /// Add or replace property instance with the given name.
65    /// Consumes and returns self (builder style).
66    pub fn property<N: Into<String>, V: Into<Value>>(self, name: N, value: V) -> Self {
67        self.insert(name.into(), value.into());
68        self
69    }
70
71    /// Returns all property names.
72    pub fn names(&self) -> PropertyNames {
73        self.iter().map(|property| property.key().clone()).collect()
74    }
75
76    // TODO: pub fn validate(&self) -> Result<(), PropertyInstancesValidationError> {}
77
78    /// Returns a sorted list of property instances.
79    pub fn to_map(&self) -> BTreeMap<String, Value> {
80        self.0.iter().map(|property| (property.key().clone(), property.value().clone())).collect()
81        // let mut tys: HashMap<String, Value> = self.0.iter().map(|ty| ty.clone()).collect();
82        // tys.sort();
83        // tys
84    }
85}
86
87impl PropertyInstanceGetter for PropertyInstances {
88    fn get<S: Into<String>>(&self, property_name: S) -> Option<Value> {
89        self.0.get(&property_name.into()).map(|property| property.value().clone())
90    }
91
92    fn as_bool<S: Into<String>>(&self, property_name: S) -> Option<bool> {
93        self.0.get(&property_name.into()).and_then(|p| p.as_bool())
94    }
95
96    fn as_u64<S: Into<String>>(&self, property_name: S) -> Option<u64> {
97        self.0.get(&property_name.into()).and_then(|p| p.as_u64())
98    }
99
100    fn as_i64<S: Into<String>>(&self, property_name: S) -> Option<i64> {
101        self.0.get(&property_name.into()).and_then(|p| p.as_i64())
102    }
103
104    fn as_f64<S: Into<String>>(&self, property_name: S) -> Option<f64> {
105        self.0.get(&property_name.into()).and_then(|p| p.as_f64())
106    }
107
108    fn as_string<S: Into<String>>(&self, property_name: S) -> Option<String> {
109        self.0.get(&property_name.into()).and_then(|p| p.as_str().map(|s| s.to_string()))
110    }
111
112    fn as_array<S: Into<String>>(&self, property_name: S) -> Option<Vec<Value>> {
113        self.0.get(&property_name.into()).and_then(|p| p.as_array().cloned())
114    }
115
116    fn as_object<S: Into<String>>(&self, property_name: S) -> Option<Map<String, Value>> {
117        self.0.get(&property_name.into()).and_then(|p| p.as_object().cloned())
118    }
119}
120
121impl MutablePropertyInstanceSetter for PropertyInstances {
122    fn set<S: Into<String>>(&mut self, property_name: S, value: Value) {
123        if let Some(mut property_value) = self.0.get_mut(&property_name.into()) {
124            let v = property_value.value_mut();
125            *v = value;
126        }
127    }
128}
129
130impl Deref for PropertyInstances {
131    type Target = DashMap<String, Value>;
132
133    fn deref(&self) -> &Self::Target {
134        &self.0
135    }
136}
137
138impl DerefMut for PropertyInstances {
139    fn deref_mut(&mut self) -> &mut Self::Target {
140        &mut self.0
141    }
142}
143
144impl IntoIterator for PropertyInstances {
145    type Item = (String, Value);
146    type IntoIter = dashmap::iter::OwningIter<String, Value>;
147    fn into_iter(self) -> Self::IntoIter {
148        self.0.into_iter()
149    }
150}
151
152impl PartialEq for PropertyInstances {
153    fn eq(&self, other: &Self) -> bool {
154        let this = self.to_map();
155        let other = other.to_map();
156        this.eq(&other)
157    }
158}
159
160impl Eq for PropertyInstances {}
161
162impl Hash for PropertyInstances {
163    fn hash<H: Hasher>(&self, hasher: &mut H) {
164        self.iter().for_each(|property| {
165            property.key().hash(hasher);
166            HashableValue(property.value()).hash(hasher);
167        });
168    }
169}
170
171impl JsonSchema for PropertyInstances {
172    fn schema_name() -> Cow<'static, str> {
173        "PropertyInstances".into()
174    }
175
176    fn json_schema(_: &mut SchemaGenerator) -> Schema {
177        json_schema!({
178            "type": "object",
179            "description": "Properties",
180        })
181    }
182}
183
184// impl From<HashMap<String, Value>> for PropertyInstances {
185//     fn from(tys: HashMap<String, Value>) -> Self {
186//         PropertyInstances(tys.into_iter().collect())
187//     }
188// }
189//
190// impl From<PropertyInstances> for HashMap<String, Value> {
191//     fn from(tys: PropertyInstances) -> Self {
192//         tys.to_map()
193//     }
194// }
195
196impl From<BTreeMap<String, Value>> for PropertyInstances {
197    fn from(tys: BTreeMap<String, Value>) -> Self {
198        PropertyInstances(tys.into_iter().collect())
199    }
200}
201
202impl From<PropertyInstances> for BTreeMap<String, Value> {
203    fn from(tys: PropertyInstances) -> Self {
204        tys.to_map()
205    }
206}
207
208impl From<&PropertyInstances> for BTreeMap<String, Value> {
209    fn from(tys: &PropertyInstances) -> Self {
210        tys.0.iter().map(|ty| (ty.key().clone(), ty.value().clone())).collect()
211    }
212}
213
214impl From<DashMap<String, Value>> for PropertyInstances {
215    fn from(tys: DashMap<String, Value>) -> Self {
216        PropertyInstances(tys)
217    }
218}
219
220impl From<&DashMap<String, Value>> for PropertyInstances {
221    fn from(tys: &DashMap<String, Value>) -> Self {
222        PropertyInstances(tys.clone())
223    }
224}
225
226impl From<PropertyInstances> for DashMap<String, Value> {
227    fn from(tys: PropertyInstances) -> Self {
228        tys.0
229    }
230}
231
232// impl From<PropertyInstances> for HashMap<String, Value> {
233//     fn from(property_instances: PropertyInstances) -> Self {
234//         property_instances.0.into_iter().collect::<HashMap<String, Value>>()
235//     }
236// }
237
238impl FromIterator<(String, Value)> for PropertyInstances {
239    fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
240        let properties = PropertyInstances::new();
241        for (property_name, property_value) in iter {
242            properties.insert(property_name, property_value);
243        }
244        properties
245    }
246}
247
248#[cfg(any(test, feature = "test"))]
249use crate::test_utils::default_from::DefaultFrom;
250#[cfg(any(test, feature = "test"))]
251use default_test::DefaultTest;
252#[cfg(any(test, feature = "test"))]
253use rand::Rng;
254#[cfg(any(test, feature = "test"))]
255use reactive_graph_utils_test::r_string;
256#[cfg(any(test, feature = "test"))]
257use serde_json::json;
258
259#[cfg(any(test, feature = "test"))]
260impl DefaultTest for PropertyInstances {
261    fn default_test() -> Self {
262        let property_instances = PropertyInstances::new();
263        let mut rng = rand::rng();
264        for _ in 0..rng.random_range(0..10) {
265            property_instances.insert(r_string(), json!(r_string()));
266        }
267        property_instances
268    }
269}
270
271#[cfg(any(test, feature = "test"))]
272impl DefaultFrom<PropertyTypes> for PropertyInstances {
273    fn default_from(property_types: &PropertyTypes) -> Self {
274        let properties = Self::new();
275        for property_type in property_types.iter() {
276            properties.insert(property_type.key().clone(), property_type.data_type.default_value_test());
277        }
278        properties
279    }
280}
281
282#[cfg(test)]
283mod tests {
284    // TODO: implement tests
285}