Skip to main content

wp_model_core/model/data/
field_ref.rs

1// FieldRef: Zero-copy wrapper for cur_name-aware field access
2
3use crate::model::Field;
4use crate::model::data::storage::FieldStorage;
5use crate::model::{DataType, Value};
6
7/// Zero-copy field reference with cur_name overlay applied.
8///
9/// `FieldRef` provides a unified view of `FieldStorage` that respects
10/// the `cur_name` override without cloning the underlying field.
11///
12/// # Performance
13///
14/// - **Zero allocation**: Only holds a reference to `FieldStorage` (8 bytes)
15/// - **Zero-copy access**: `get_name()`, `get_value()` etc. don't clone
16/// - **Lazy owned conversion**: Cloning happens only when calling `to_owned()`
17///
18/// # Examples
19///
20/// ```ignore
21/// use std::sync::Arc;
22/// use wp_model_core::model::{Field, FieldStorage, Value, DataType};
23///
24/// let field = Arc::new(Field::new(DataType::Chars, "original", Value::from("data")));
25/// let mut storage = FieldStorage::from_shared(field);
26/// storage.set_name("renamed");
27///
28/// let field_ref = storage.field_ref();
29/// assert_eq!(field_ref.get_name(), "renamed");
30/// assert_eq!(field_ref.get_value(), &Value::from("data"));
31/// ```
32#[derive(Clone, Copy)]
33pub struct FieldRef<'a> {
34    pub(crate) storage: &'a FieldStorage,
35}
36
37impl<'a> FieldRef<'a> {
38    /// Get effective field name (cur_name takes priority).
39    #[inline]
40    pub fn get_name(&self) -> &'a str {
41        self.storage.get_name()
42    }
43
44    /// Get field metadata type.
45    #[inline]
46    pub fn get_meta(&self) -> &'a DataType {
47        self.storage.as_field().get_meta()
48    }
49
50    /// Get field value reference.
51    #[inline]
52    pub fn get_value(&self) -> &'a Value {
53        self.storage.as_field().get_value()
54    }
55
56    /// Convert to owned field with cur_name applied.
57    ///
58    /// Clones the underlying field and applies the `cur_name` override if present.
59    pub fn to_owned(self) -> Field<Value> {
60        self.storage.clone().into_owned()
61    }
62
63    /// Check if this field is shared (Arc-backed).
64    #[inline]
65    pub fn is_shared(&self) -> bool {
66        self.storage.is_shared()
67    }
68
69    /// Check if cur_name override is active.
70    #[inline]
71    pub fn has_name_override(&self) -> bool {
72        self.storage.cur_name.is_some()
73    }
74
75    /// Get Arc reference count for debugging.
76    ///
77    /// Returns `Some(count)` for `Shared` variant, `None` for `Owned`.
78    #[inline]
79    pub fn shared_count(&self) -> Option<usize> {
80        self.storage.shared_count()
81    }
82}
83
84// ==================== Trait Implementations ====================
85
86impl<'a> std::fmt::Debug for FieldRef<'a> {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        f.debug_struct("FieldRef")
89            .field("name", &self.get_name())
90            .field("meta", &self.get_meta())
91            .field("value", &self.get_value())
92            .field("is_shared", &self.is_shared())
93            .field("has_override", &self.has_name_override())
94            .finish()
95    }
96}
97
98impl<'a> std::fmt::Display for FieldRef<'a> {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        write!(f, "{}={:?}", self.get_name(), self.get_value())
101    }
102}
103
104impl<'a> PartialEq<Field<Value>> for FieldRef<'a> {
105    fn eq(&self, other: &Field<Value>) -> bool {
106        self.get_name() == other.get_name()
107            && self.get_meta() == other.get_meta()
108            && self.get_value() == other.get_value()
109    }
110}
111
112impl<'a, 'b> PartialEq<FieldRef<'b>> for FieldRef<'a> {
113    fn eq(&self, other: &FieldRef<'b>) -> bool {
114        self.get_name() == other.get_name()
115            && self.get_meta() == other.get_meta()
116            && self.get_value() == other.get_value()
117    }
118}
119
120impl<'a> PartialEq<FieldRef<'a>> for Field<Value> {
121    fn eq(&self, other: &FieldRef<'a>) -> bool {
122        other == self
123    }
124}
125
126// ==================== Tests ====================
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use std::sync::Arc;
132
133    #[test]
134    fn test_field_ref_respects_cur_name() {
135        let field = Field::new(DataType::Chars, "old_name", Value::from("value"));
136        let mut storage = FieldStorage::from_owned(field);
137        storage.set_name("new_name");
138
139        let field_ref = storage.field_ref();
140
141        assert_eq!(field_ref.get_name(), "new_name");
142        assert_eq!(field_ref.get_value(), &Value::from("value"));
143
144        // Underlying field unchanged
145        assert_eq!(storage.as_field().get_name(), "old_name");
146    }
147
148    #[test]
149    fn test_field_ref_no_cur_name() {
150        let field = Field::new(DataType::Digit, "count", Value::from(42));
151        let storage = FieldStorage::from_owned(field);
152
153        let field_ref = storage.field_ref();
154
155        assert_eq!(field_ref.get_name(), "count");
156        assert_eq!(field_ref.get_value(), &Value::from(42));
157        assert!(!field_ref.has_name_override());
158    }
159
160    #[test]
161    fn test_field_ref_shared_zero_copy() {
162        let field = Arc::new(Field::new(DataType::Chars, "original", Value::from("data")));
163        let arc_count_before = Arc::strong_count(&field);
164
165        let mut storage = FieldStorage::from_shared(field.clone());
166        storage.set_name("renamed");
167
168        // field_ref() doesn't clone Arc
169        let field_ref = storage.field_ref();
170        assert_eq!(Arc::strong_count(&field), arc_count_before + 1);
171
172        assert_eq!(field_ref.get_name(), "renamed");
173        assert_eq!(field_ref.get_value(), &Value::from("data"));
174        assert!(field_ref.is_shared());
175        assert!(field_ref.has_name_override());
176    }
177
178    #[test]
179    fn test_field_ref_to_owned() {
180        let mut storage =
181            FieldStorage::from_owned(Field::new(DataType::Digit, "count", Value::from(42)));
182        storage.set_name("total");
183
184        let field_ref = storage.field_ref();
185        let owned = field_ref.to_owned();
186
187        assert_eq!(owned.get_name(), "total");
188        assert_eq!(owned.get_value(), &Value::from(42));
189    }
190
191    #[test]
192    fn test_field_ref_shared_count() {
193        let field = Arc::new(Field::new(DataType::Chars, "name", Value::from("test")));
194        let storage1 = FieldStorage::from_shared(field.clone());
195        let storage2 = FieldStorage::from_shared(field.clone());
196
197        assert_eq!(storage1.field_ref().shared_count(), Some(3));
198        assert_eq!(storage2.field_ref().shared_count(), Some(3));
199    }
200
201    #[test]
202    fn test_field_ref_owned_no_shared_count() {
203        let storage =
204            FieldStorage::from_owned(Field::new(DataType::Chars, "name", Value::from("test")));
205
206        assert_eq!(storage.field_ref().shared_count(), None);
207    }
208
209    #[test]
210    fn test_field_ref_equality() {
211        let field1 = Field::new(DataType::Chars, "name", Value::from("Alice"));
212        let storage1 = FieldStorage::from_owned(field1.clone());
213
214        let field2 = Field::new(DataType::Chars, "name", Value::from("Alice"));
215        let storage2 = FieldStorage::from_owned(field2.clone());
216
217        assert_eq!(storage1.field_ref(), storage2.field_ref());
218        assert_eq!(storage1.field_ref(), field1);
219        assert_eq!(field2, storage2.field_ref());
220    }
221
222    #[test]
223    fn test_field_ref_inequality_with_cur_name() {
224        let mut storage1 =
225            FieldStorage::from_owned(Field::new(DataType::Chars, "name", Value::from("Alice")));
226        storage1.set_name("renamed");
227
228        let storage2 =
229            FieldStorage::from_owned(Field::new(DataType::Chars, "name", Value::from("Alice")));
230
231        assert_ne!(storage1.field_ref(), storage2.field_ref());
232    }
233
234    #[test]
235    fn test_field_ref_debug() {
236        let mut storage =
237            FieldStorage::from_owned(Field::new(DataType::Chars, "test", Value::from("value")));
238        storage.set_name("renamed");
239
240        let debug = format!("{:?}", storage.field_ref());
241        assert!(debug.contains("renamed"));
242        assert!(debug.contains("Chars"));
243        assert!(debug.contains("has_override: true"));
244    }
245
246    #[test]
247    fn test_field_ref_display() {
248        let storage =
249            FieldStorage::from_owned(Field::new(DataType::Digit, "count", Value::from(42)));
250
251        let display = format!("{}", storage.field_ref());
252        assert_eq!(display, "count=Digit(42)");
253    }
254
255    #[test]
256    fn test_field_ref_copy() {
257        let storage =
258            FieldStorage::from_owned(Field::new(DataType::Chars, "name", Value::from("test")));
259
260        let ref1 = storage.field_ref();
261        let ref2 = ref1; // Copy
262
263        assert_eq!(ref1.get_name(), ref2.get_name());
264    }
265}