libreda_db/
property_storage.rs

1// Copyright (c) 2020-2021 Thomas Kramer.
2// SPDX-FileCopyrightText: 2022 Thomas Kramer
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Container structs for user defined properties.
7
8use crate::rc_string::RcString;
9use std::borrow::Borrow;
10use std::collections::HashMap;
11use std::convert::TryInto;
12use std::hash::Hash;
13use std::sync::Arc;
14
15// trait AnyValue: Any + Clone + std::fmt::Debug {}
16
17/// Property value type.
18/// Properties can hold different types that are encapsulated in this enum.
19#[derive(Debug, Clone)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub enum PropertyValue {
22    /// Property is a string.
23    String(RcString),
24    /// Property is a byte string.
25    Bytes(Vec<u8>),
26    /// Property is a signed integer.
27    SInt(i32),
28    /// Property is an unsigned integer.
29    UInt(u32),
30    /// Property is a float.
31    Float(f64),
32    // /// Dynamically typed value.
33    // Any(Box<dyn AnyValue>),
34}
35
36impl PropertyValue {
37    /// Try to get a string value.
38    pub fn get_string(&self) -> Option<RcString> {
39        match self {
40            PropertyValue::String(s) => Some(s.clone()),
41            _ => None,
42        }
43    }
44
45    /// Try to get a `&str` value. Works for `String` property values.
46    pub fn get_str(&self) -> Option<&str> {
47        match self {
48            PropertyValue::String(s) => Some(s.as_str()),
49            _ => None,
50        }
51    }
52
53    /// Try to get a byte string value.
54    pub fn get_bytes(&self) -> Option<&Vec<u8>> {
55        match self {
56            PropertyValue::Bytes(s) => Some(s),
57            _ => None,
58        }
59    }
60
61    /// Try to get a float value.
62    pub fn get_float(&self) -> Option<f64> {
63        match self {
64            PropertyValue::Float(v) => Some(*v),
65            _ => None,
66        }
67    }
68
69    /// Try to get an i32 value.
70    pub fn get_sint(&self) -> Option<i32> {
71        match self {
72            PropertyValue::SInt(v) => Some(*v),
73            _ => None,
74        }
75    }
76
77    /// Try to get an i32 value.
78    pub fn get_uint(&self) -> Option<u32> {
79        match self {
80            PropertyValue::UInt(v) => Some(*v),
81            _ => None,
82        }
83    }
84
85    // /// Try to get a dynamically typed value.
86    // pub fn get_any(&self) -> Option<&Box<dyn AnyValue>> {
87    //     match self {
88    //         PropertyValue::Any(v) => Some(v),
89    //         _ => None
90    //     }
91    // }
92}
93
94// pub enum PropertyKey {
95//     String(String),
96//
97// }
98
99impl From<String> for PropertyValue {
100    fn from(v: String) -> Self {
101        PropertyValue::String(v.into())
102    }
103}
104
105impl From<Arc<String>> for PropertyValue {
106    fn from(v: Arc<String>) -> Self {
107        PropertyValue::String(v.into())
108    }
109}
110
111impl From<&Arc<String>> for PropertyValue {
112    fn from(v: &Arc<String>) -> Self {
113        PropertyValue::String(v.into())
114    }
115}
116
117impl From<&str> for PropertyValue {
118    fn from(v: &str) -> Self {
119        PropertyValue::String(v.into())
120    }
121}
122
123impl From<Vec<u8>> for PropertyValue {
124    fn from(v: Vec<u8>) -> Self {
125        PropertyValue::Bytes(v)
126    }
127}
128
129impl<'a> TryInto<&'a str> for &'a PropertyValue {
130    type Error = ();
131
132    fn try_into(self) -> Result<&'a str, Self::Error> {
133        if let PropertyValue::String(s) = self {
134            Ok(s.as_str())
135        } else {
136            Err(())
137        }
138    }
139}
140
141impl From<i32> for PropertyValue {
142    fn from(v: i32) -> Self {
143        PropertyValue::SInt(v)
144    }
145}
146
147impl TryInto<i32> for &PropertyValue {
148    type Error = ();
149
150    fn try_into(self) -> Result<i32, Self::Error> {
151        if let PropertyValue::SInt(v) = self {
152            Ok(*v)
153        } else {
154            Err(())
155        }
156    }
157}
158
159impl From<u32> for PropertyValue {
160    fn from(v: u32) -> Self {
161        PropertyValue::UInt(v)
162    }
163}
164
165impl From<f64> for PropertyValue {
166    fn from(v: f64) -> Self {
167        PropertyValue::Float(v)
168    }
169}
170
171// impl From<Box<dyn Any>> for PropertyValue {
172//     fn from(v: Box<dyn Any>) -> Self {
173//         PropertyValue::Any(v)
174//     }
175// }
176
177/// Look-up table for property values.
178#[derive(Debug, Clone)]
179#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
180pub struct PropertyStore<K>
181where
182    K: Hash + Eq,
183{
184    content: HashMap<K, PropertyValue>,
185}
186
187impl<K: Hash + Eq> Default for PropertyStore<K> {
188    fn default() -> Self {
189        Self::new()
190    }
191}
192
193impl<K: Hash + Eq> PropertyStore<K> {
194    /// Create an empty property store.
195    pub fn new() -> Self {
196        PropertyStore {
197            content: HashMap::new(),
198        }
199    }
200
201    /// Insert a property.
202    /// Returns the old property value if there was already a property stored under this key.
203    pub fn insert<V: Into<PropertyValue>>(&mut self, key: K, value: V) -> Option<PropertyValue> {
204        self.content.insert(key, value.into())
205    }
206
207    /// Get a property value by the property key.
208    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&PropertyValue>
209    where
210        K: Borrow<Q>,
211        Q: Eq + Hash,
212    {
213        self.content.get(key)
214    }
215
216    /// Check if the `key` is contained in this property store.
217    pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
218    where
219        K: Borrow<Q>,
220        Q: Eq + Hash,
221    {
222        self.content.contains_key(key)
223    }
224
225    /// Get a string property value by key.
226    /// If the property value is not a string `None` is returned.
227    pub fn get_string<Q: ?Sized>(&self, key: &Q) -> Option<&RcString>
228    where
229        K: Borrow<Q>,
230        Q: Eq + Hash,
231    {
232        self.get(key).and_then(|v| {
233            if let PropertyValue::String(s) = v {
234                Some(s)
235            } else {
236                None
237            }
238        })
239    }
240}
241
242/// A trait for associating user defined properties with a type.
243pub trait WithProperties {
244    /// Property key type.
245    type Key: Hash + Eq;
246
247    /// Call a function with maybe the property storage as argument.
248    ///
249    /// The property store might not always be initialized. For instance for
250    /// objects without any defined properties, it will likely be `None`.
251    fn with_properties<F, R>(&self, f: F) -> R
252    where
253        F: FnOnce(Option<&PropertyStore<Self::Key>>) -> R;
254
255    /// Get mutable reference to the property storage.
256    fn with_properties_mut<F, R>(&self, f: F) -> R
257    where
258        F: FnOnce(&mut PropertyStore<Self::Key>) -> R;
259
260    /// Get a property value by the property key.
261    fn property<Q: ?Sized>(&self, key: &Q) -> Option<PropertyValue>
262    where
263        Self::Key: Borrow<Q>,
264        Q: Eq + Hash,
265    {
266        self.with_properties(|p| p.and_then(|p| p.get(key).cloned()))
267    }
268
269    /// Get a string property value by key.
270    /// If the property value is not a string `None` is returned.
271    fn property_str<Q: ?Sized>(&self, key: &Q) -> Option<RcString>
272    where
273        Self::Key: Borrow<Q>,
274        Q: Eq + Hash,
275    {
276        self.with_properties(|p| p.and_then(|p| p.get_string(key).cloned()))
277    }
278
279    /// Insert a property.
280    /// Returns the old property value if there was already a property stored under this key.
281    fn set_property<V: Into<PropertyValue>>(
282        &self,
283        key: Self::Key,
284        value: V,
285    ) -> Option<PropertyValue> {
286        self.with_properties_mut(|p| p.insert(key, value))
287    }
288}