Skip to main content

wp_model_core/model/data/
storage.rs

1use crate::model::Value;
2use crate::model::DataType;
3use crate::model::{FNameStr, FValueStr};
4use crate::model::format::LevelFormatAble;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use std::fmt::{Display, Formatter};
7use std::net::IpAddr;
8use std::sync::Arc;
9
10use super::field::Field;
11use super::record::{RecordItem, RecordItemFactory};
12
13/// Mixed storage for fields supporting both Arc (zero-copy) and owned (dynamic) variants.
14///
15/// This enum allows efficient handling of both static/constant fields (via `Shared`)
16/// and dynamically computed fields (via `Owned`).
17///
18/// # Performance
19/// - Cloning `Shared` variant: ~5ns (Arc reference count increment)
20/// - Cloning `Owned` variant: 50-500ns (deep copy of Field<Value>)
21/// - Accessing field via `as_field()`: ~0-1ns
22///
23/// # Examples
24///
25/// ```ignore
26/// use std::sync::Arc;
27/// use wp_model_core::model::{FieldStorage, Field, Value, DataType};
28///
29/// // Create a shared field (for static/constant values)
30/// let static_field = Field::new(DataType::Chars, "app_name", Value::from("myapp"));
31/// let shared = FieldStorage::from_shared(static_field);
32///
33/// // Clone is cheap (only increments Arc reference count)
34/// let shared2 = shared.clone();
35///
36/// // Create an owned field (for dynamic values)
37/// let dynamic_field = Field::new(DataType::Digit, "counter", Value::from(42));
38/// let owned = FieldStorage::from_owned(dynamic_field);
39/// ```
40#[derive(Clone, Debug)]
41pub enum FieldStorage {
42    /// Shared field with reference counting for zero-copy sharing.
43    ///
44    /// Used for static/constant fields that are referenced multiple times.
45    /// Cloning this variant only increments the reference count (~5ns).
46    Shared(Arc<Field<Value>>),
47
48    /// Owned field from dynamic computation.
49    ///
50    /// Used for fields that are computed dynamically per record.
51    /// Cloning this variant performs a deep copy of the field.
52    Owned(Field<Value>),
53}
54
55impl FieldStorage {
56    /// Get a reference to the underlying field (unified interface).
57    ///
58    /// # Performance
59    /// - Shared variant: ~1ns (dereference)
60    /// - Owned variant: 0ns (direct reference)
61    ///
62    /// # Examples
63    ///
64    /// ```ignore
65    /// use wp_model_core::model::{FieldStorage, Field, Value, DataType};
66    ///
67    /// let field = Field::new(DataType::Chars, "test", Value::from("value"));
68    /// let storage = FieldStorage::from_owned(field);
69    ///
70    /// assert_eq!(storage.as_field().get_name(), "test");
71    /// ```
72    #[inline]
73    pub fn as_field(&self) -> &Field<Value> {
74        match self {
75            FieldStorage::Shared(arc) => arc.as_ref(),
76            FieldStorage::Owned(field) => field,
77        }
78    }
79
80    /// Convert to owned Field<Value>.
81    ///
82    /// # Performance
83    /// - Shared variant: ~50-500ns (clone the inner field if multiple references exist)
84    /// - Owned variant: 0ns (move)
85    ///
86    /// # Examples
87    ///
88    /// ```ignore
89    /// use std::sync::Arc;
90    /// use wp_model_core::model::{FieldStorage, Field, Value, DataType};
91    ///
92    /// let field = Field::new(DataType::Digit, "num", Value::from(42));
93    /// let storage = FieldStorage::from_owned(field);
94    /// let owned = storage.into_owned();
95    ///
96    /// assert_eq!(owned.get_name(), "num");
97    /// ```
98    pub fn into_owned(self) -> Field<Value> {
99        match self {
100            FieldStorage::Shared(arc) => {
101                // Try to unwrap if this is the only reference, otherwise clone
102                Arc::try_unwrap(arc).unwrap_or_else(|arc| (*arc).clone())
103            }
104            FieldStorage::Owned(field) => field,
105        }
106    }
107
108    /// Create Shared variant from owned field.
109    ///
110    /// # Examples
111    ///
112    /// ```ignore
113    /// use wp_model_core::model::{FieldStorage, Field, Value, DataType};
114    ///
115    /// let field = Field::new(DataType::Chars, "name", Value::from("Alice"));
116    /// let storage = FieldStorage::from_shared(field);
117    ///
118    /// assert!(storage.is_shared());
119    /// ```
120    pub fn from_shared(field: Field<Value>) -> Self {
121        FieldStorage::Shared(Arc::new(field))
122    }
123
124    /// Create Owned variant from owned field.
125    ///
126    /// # Examples
127    ///
128    /// ```ignore
129    /// use wp_model_core::model::{FieldStorage, Field, Value, DataType};
130    ///
131    /// let field = Field::new(DataType::Digit, "count", Value::from(10));
132    /// let storage = FieldStorage::from_owned(field);
133    ///
134    /// assert!(!storage.is_shared());
135    /// ```
136    pub fn from_owned(field: Field<Value>) -> Self {
137        FieldStorage::Owned(field)
138    }
139
140    /// Check if this is a Shared variant.
141    ///
142    /// # Examples
143    ///
144    /// ```ignore
145    /// use wp_model_core::model::{FieldStorage, Field, Value, DataType};
146    ///
147    /// let field = Field::new(DataType::Chars, "test", Value::from("data"));
148    /// let shared = FieldStorage::from_shared(field.clone());
149    /// let owned = FieldStorage::from_owned(field);
150    ///
151    /// assert!(shared.is_shared());
152    /// assert!(!owned.is_shared());
153    /// ```
154    #[inline]
155    pub fn is_shared(&self) -> bool {
156        matches!(self, FieldStorage::Shared(_))
157    }
158
159    /// Get Arc reference count (for debugging/diagnostics).
160    ///
161    /// Returns `Some(count)` for Shared variant, `None` for Owned variant.
162    ///
163    /// # Examples
164    ///
165    /// ```ignore
166    /// use wp_model_core::model::{FieldStorage, Field, Value, DataType};
167    ///
168    /// let field = Field::new(DataType::Digit, "x", Value::from(1));
169    /// let shared = FieldStorage::from_shared(field);
170    ///
171    /// assert_eq!(shared.shared_count(), Some(1));
172    ///
173    /// let shared2 = shared.clone();
174    /// assert_eq!(shared.shared_count(), Some(2));
175    /// ```
176    pub fn shared_count(&self) -> Option<usize> {
177        match self {
178            FieldStorage::Shared(arc) => Some(Arc::strong_count(arc)),
179            FieldStorage::Owned(_) => None,
180        }
181    }
182}
183
184// Implement Display by delegating to inner field
185impl Display for FieldStorage {
186    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187        self.as_field().fmt(f)
188    }
189}
190
191// Implement PartialEq by comparing the underlying fields
192impl PartialEq for FieldStorage {
193    fn eq(&self, other: &Self) -> bool {
194        self.as_field().eq(other.as_field())
195    }
196}
197
198impl Eq for FieldStorage {}
199
200// Serialize: Transparently serialize as Field<Value>
201impl Serialize for FieldStorage {
202    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
203    where
204        S: Serializer,
205    {
206        // Both variants serialize the same way (as Field<Value>)
207        self.as_field().serialize(serializer)
208    }
209}
210
211// Deserialize: Always deserialize to Owned variant
212impl<'de> Deserialize<'de> for FieldStorage {
213    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
214    where
215        D: Deserializer<'de>,
216    {
217        // Deserialize as Field<Value> and wrap in Owned variant
218        Field::<Value>::deserialize(deserializer).map(FieldStorage::Owned)
219    }
220}
221
222// Implement RecordItem trait by delegating to the inner field
223impl RecordItem for FieldStorage {
224    fn get_name(&self) -> &str {
225        self.as_field().get_name()
226    }
227
228    fn get_meta(&self) -> &DataType {
229        self.as_field().get_meta()
230    }
231
232    fn get_value(&self) -> &Value {
233        self.as_field().get_value()
234    }
235
236    fn get_value_mut(&mut self) -> &mut Value {
237        match self {
238            FieldStorage::Shared(arc) => {
239                // If there are multiple references, we need to clone to get a mutable version
240                // This converts Shared to Owned variant
241                let field = Arc::try_unwrap(std::mem::replace(arc, Arc::new(Field::new(DataType::Ignore, "", Value::from(false)))))
242                    .unwrap_or_else(|arc| (*arc).clone());
243                *self = FieldStorage::Owned(field);
244                match self {
245                    FieldStorage::Owned(f) => f.get_value_mut(),
246                    _ => unreachable!(),
247                }
248            }
249            FieldStorage::Owned(field) => field.get_value_mut(),
250        }
251    }
252}
253
254// Implement RecordItemFactory trait
255impl RecordItemFactory for FieldStorage {
256    fn from_digit<S: Into<FNameStr>>(name: S, val: i64) -> Self {
257        FieldStorage::Owned(Field::from_digit(name, val))
258    }
259
260    fn from_ip<S: Into<FNameStr>>(name: S, ip: IpAddr) -> Self {
261        FieldStorage::Owned(Field::from_ip(name, ip))
262    }
263
264    fn from_chars<N: Into<FNameStr>, Val: Into<FValueStr>>(name: N, val: Val) -> Self {
265        FieldStorage::Owned(Field::from_chars(name, val))
266    }
267}
268
269// Implement LevelFormatAble by delegating to inner field
270impl LevelFormatAble for FieldStorage {
271    fn level_fmt(&self, f: &mut Formatter<'_>, level: usize) -> std::fmt::Result {
272        self.as_field().level_fmt(f, level)
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279    use crate::model::DataType;
280
281    #[test]
282    fn test_field_storage_shared_variant() {
283        let field = Field::new(DataType::Chars, "test", Value::from("hello"));
284        let storage = FieldStorage::Shared(Arc::new(field.clone()));
285
286        // as_field returns correct reference
287        assert_eq!(storage.as_field().get_name(), "test");
288        assert!(storage.is_shared());
289        assert_eq!(storage.shared_count(), Some(1));
290
291        // Clone Shared variant only increments reference count
292        let storage2 = storage.clone();
293        assert_eq!(storage.shared_count(), Some(2));
294        assert_eq!(storage2.shared_count(), Some(2));
295    }
296
297    #[test]
298    fn test_field_storage_owned_variant() {
299        let field = Field::new(DataType::Digit, "test", Value::from(42));
300        let storage = FieldStorage::Owned(field);
301
302        assert_eq!(storage.as_field().get_name(), "test");
303        assert!(!storage.is_shared());
304        assert_eq!(storage.shared_count(), None);
305    }
306
307    #[test]
308    fn test_into_owned() {
309        // Shared variant
310        let field1 = Field::new(DataType::Chars, "shared_field", Value::from("value"));
311        let storage1 = FieldStorage::Shared(Arc::new(field1));
312        let owned1 = storage1.into_owned();
313        assert_eq!(owned1.get_name(), "shared_field");
314
315        // Owned variant
316        let field2 = Field::new(DataType::Digit, "owned_field", Value::from(123));
317        let storage2 = FieldStorage::Owned(field2);
318        let owned2 = storage2.into_owned();
319        assert_eq!(owned2.get_name(), "owned_field");
320    }
321
322    #[test]
323    fn test_from_shared() {
324        let field = Field::new(DataType::Chars, "name", Value::from("Alice"));
325        let storage = FieldStorage::from_shared(field);
326
327        assert!(storage.is_shared());
328        assert_eq!(storage.as_field().get_name(), "name");
329    }
330
331    #[test]
332    fn test_from_owned() {
333        let field = Field::new(DataType::Digit, "count", Value::from(10));
334        let storage = FieldStorage::from_owned(field);
335
336        assert!(!storage.is_shared());
337        assert_eq!(storage.as_field().get_name(), "count");
338    }
339
340    #[test]
341    fn test_display() {
342        let field = Field::new(DataType::Digit, "num", Value::from(42));
343        let storage = FieldStorage::from_owned(field);
344
345        let display = format!("{}", storage);
346        assert!(display.contains("42"));
347    }
348
349    #[test]
350    fn test_equality() {
351        let field1 = Field::new(DataType::Chars, "test", Value::from("value"));
352        let field2 = Field::new(DataType::Chars, "test", Value::from("value"));
353        let field3 = Field::new(DataType::Chars, "test", Value::from("different"));
354
355        let shared1 = FieldStorage::from_shared(field1.clone());
356        let owned1 = FieldStorage::from_owned(field1);
357        let shared2 = FieldStorage::from_shared(field2);
358        let owned3 = FieldStorage::from_owned(field3);
359
360        // Same content should be equal regardless of storage type
361        assert_eq!(shared1, owned1);
362        assert_eq!(shared1, shared2);
363
364        // Different content should not be equal
365        assert_ne!(shared1, owned3);
366    }
367
368    #[test]
369    fn test_serde_serialization() {
370        let field1 = Field::new(DataType::Chars, "f1", Value::from("shared"));
371        let field2 = Field::new(DataType::Digit, "f2", Value::from(99));
372
373        let shared = FieldStorage::from_shared(field1);
374        let owned = FieldStorage::from_owned(field2);
375
376        // Serialize
377        let json_shared = serde_json::to_string(&shared).unwrap();
378        let json_owned = serde_json::to_string(&owned).unwrap();
379
380        // Deserialize (should always get Owned variant)
381        let deserialized_shared: FieldStorage = serde_json::from_str(&json_shared).unwrap();
382        let deserialized_owned: FieldStorage = serde_json::from_str(&json_owned).unwrap();
383
384        // Verify values are correct
385        assert_eq!(deserialized_shared.as_field().get_name(), "f1");
386        assert_eq!(deserialized_owned.as_field().get_name(), "f2");
387
388        // Verify both are Owned variant after deserialization
389        assert!(!deserialized_shared.is_shared());
390        assert!(!deserialized_owned.is_shared());
391    }
392
393    #[test]
394    fn test_clone_performance_difference() {
395        // This is more of a documentation test showing the usage pattern
396        use crate::model::FValueStr;
397        let large_str = FValueStr::from("x".repeat(1000));
398        let field = Field::new(DataType::Chars, "large", Value::from(large_str));
399
400        // Shared: cheap clone
401        let shared = FieldStorage::from_shared(field.clone());
402        let _shared2 = shared.clone();
403        assert_eq!(shared.shared_count(), Some(2));
404
405        // Owned: deep clone
406        let owned = FieldStorage::from_owned(field);
407        let _owned2 = owned.clone();
408        assert!(owned.shared_count().is_none());
409    }
410}