wp_model_core/model/data/
field.rs

1use crate::model::DataType;
2use crate::model::format::LevelFormatAble;
3use crate::model::{FNameStr, FValueStr};
4
5use crate::model::Value;
6use crate::traits::AsValueRef;
7use serde::Deserialize;
8use serde::Serialize;
9use std::fmt::{Display, Formatter};
10use std::rc::Rc;
11use std::sync::Arc;
12
13#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
14pub struct Field<T> {
15    pub meta: DataType,
16    pub name: FNameStr,
17    pub value: T,
18}
19
20/// A trait for getting an immutable reference to the inner value of a Field
21pub trait ValueRef<T> {
22    /// Returns an immutable reference to the inner value
23    fn value_ref(&self) -> &T;
24}
25
26impl<T> ValueRef<T> for Field<T> {
27    fn value_ref(&self) -> &T {
28        &self.value
29    }
30}
31
32impl<T> From<Field<T>> for Field<Rc<T>> {
33    fn from(other: Field<T>) -> Self {
34        let value = Rc::<T>::from(other.value);
35        Self {
36            meta: other.meta,
37            name: other.name,
38            value,
39        }
40    }
41}
42
43impl<T> From<Field<T>> for Field<Arc<T>> {
44    fn from(other: Field<T>) -> Self {
45        let value = Arc::<T>::from(other.value);
46        Self {
47            meta: other.meta,
48            name: other.name,
49            value,
50        }
51    }
52}
53
54impl<T> Field<T> {
55    pub fn new<S: Into<FNameStr>, V: Into<T>>(meta: DataType, name: S, value: V) -> Self {
56        Self {
57            meta,
58            name: name.into(),
59            value: value.into(),
60        }
61    }
62
63    pub fn new_opt(meta: DataType, name: Option<FNameStr>, value: T) -> Self {
64        let name = name.unwrap_or_else(|| FNameStr::from(String::from(&meta)));
65        Field { meta, name, value }
66    }
67
68    pub fn get_name(&self) -> &str {
69        self.name.as_str()
70    }
71    pub fn clone_name(&self) -> String {
72        self.name.as_str().to_string()
73    }
74    pub fn get_meta(&self) -> &DataType {
75        &self.meta
76    }
77
78    pub fn set_name<S: Into<FNameStr>>(&mut self, name: S) {
79        self.name = name.into()
80    }
81}
82
83impl Field<Value> {
84    pub fn from_shared_chars<S: Into<FNameStr>>(name: S, val: FValueStr) -> Self {
85        Self::new(DataType::Chars, name.into(), Value::Chars(val))
86    }
87
88    pub fn get_chars(&self) -> Option<&str> {
89        self.value.as_str()
90    }
91
92    pub fn get_chars_mut(&mut self) -> Option<&mut FValueStr> {
93        self.value.ensure_owned_chars()
94    }
95}
96impl<T> Field<T>
97where
98    T: AsValueRef<Value>,
99{
100    pub fn get_value(&self) -> &Value {
101        self.value.as_value_ref()
102        //&self.value
103    }
104
105    pub fn get_value_mut(&mut self) -> &mut Value {
106        self.value.as_value_mutref()
107    }
108}
109
110impl<T> LevelFormatAble for Field<T>
111where
112    T: Display,
113{
114    fn level_fmt(&self, f: &mut Formatter<'_>, level: usize) -> std::fmt::Result {
115        let meta: String = From::from(&self.meta);
116        writeln!(
117            f,
118            "{:width$}[{:<16}] {:<20} : {}",
119            "",
120            meta,
121            self.name,
122            self.value,
123            width = level * 6
124        )?;
125        Ok(())
126    }
127}
128
129impl<T> Display for Field<T>
130where
131    T: Display,
132{
133    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
134        write!(f, "{}({})", self.meta, self.value)
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use crate::model::{DataField, FValueStr};
142
143    // ========== Field creation tests ==========
144
145    #[test]
146    fn test_field_new() {
147        let field: Field<i64> = Field::new(DataType::Digit, "count", 42i64);
148        assert_eq!(field.meta, DataType::Digit);
149        assert_eq!(field.get_name(), "count");
150        assert_eq!(field.value, 42);
151    }
152
153    #[test]
154    fn test_field_new_with_string_conversion() {
155        let field: Field<String> = Field::new(DataType::Chars, String::from("key"), "value");
156        assert_eq!(field.get_name(), "key");
157        assert_eq!(field.value, "value");
158    }
159
160    #[test]
161    fn test_field_new_opt_with_name() {
162        let field: Field<i64> = Field::new_opt(DataType::Digit, Some("num".into()), 100);
163        assert_eq!(field.get_name(), "num");
164        assert_eq!(field.value, 100);
165    }
166
167    #[test]
168    fn test_field_new_opt_without_name() {
169        let field: Field<i64> = Field::new_opt(DataType::Digit, None, 50);
170        // When name is None, it should use meta's string representation
171        assert_eq!(field.get_name(), "digit");
172        assert_eq!(field.value, 50);
173    }
174
175    // ========== Field accessor tests ==========
176
177    #[test]
178    fn test_field_get_name() {
179        let field: Field<i64> = Field::new(DataType::Digit, "test_name", 1);
180        assert_eq!(field.get_name(), "test_name");
181    }
182
183    #[test]
184    fn test_field_clone_name() {
185        let field: Field<i64> = Field::new(DataType::Digit, "original", 1);
186        let cloned = field.clone_name();
187        assert_eq!(cloned, "original");
188        // Verify it's a new String, not a reference
189        assert_eq!(cloned, field.get_name());
190    }
191
192    #[test]
193    fn test_field_get_meta() {
194        let field: Field<i64> = Field::new(DataType::Float, "val", 1);
195        assert_eq!(field.get_meta(), &DataType::Float);
196    }
197
198    #[test]
199    fn test_field_set_name() {
200        let mut field: Field<i64> = Field::new(DataType::Digit, "old_name", 1);
201        field.set_name("new_name");
202        assert_eq!(field.get_name(), "new_name");
203    }
204
205    // ========== ValueRef trait tests ==========
206
207    #[test]
208    fn test_value_ref_trait() {
209        let field: Field<i64> = Field::new(DataType::Digit, "num", 42);
210        assert_eq!(field.value_ref(), &42);
211    }
212
213    // ========== Field<Value> specific tests ==========
214
215    #[test]
216    fn test_field_get_value() {
217        let field: DataField = Field::new(DataType::Digit, "num", Value::Digit(99));
218        assert_eq!(field.get_value(), &Value::Digit(99));
219    }
220
221    #[test]
222    fn test_field_get_value_mut() {
223        let mut field: DataField = Field::new(DataType::Digit, "num", Value::Digit(10));
224        *field.get_value_mut() = Value::Digit(20);
225        assert_eq!(field.get_value(), &Value::Digit(20));
226    }
227
228    #[test]
229    fn test_field_from_shared_chars() {
230        let s = FValueStr::from("hello");
231        let field: DataField = Field::from_shared_chars("msg", s.clone());
232        assert_eq!(field.get_name(), "msg");
233        assert_eq!(field.get_meta(), &DataType::Chars);
234        assert!(matches!(field.value, Value::Chars(_)));
235        assert_eq!(field.get_chars(), Some("hello"));
236    }
237
238    #[test]
239    fn test_field_get_chars_mut() {
240        let s = FValueStr::from("foo");
241        let mut field: DataField = Field::from_shared_chars("msg", s);
242        {
243            let value = field.get_chars_mut().expect("mutable");
244            // SmolStr is immutable, so we replace it with a new one
245            *value = FValueStr::from(format!("{}-bar", value.as_str()));
246        }
247        assert!(matches!(field.value, Value::Chars(_)));
248        assert_eq!(field.get_chars(), Some("foo-bar"));
249    }
250
251    // ========== From conversions tests ==========
252
253    #[test]
254    fn test_field_to_rc() {
255        let field: Field<i64> = Field::new(DataType::Digit, "num", 42);
256        let rc_field: Field<Rc<i64>> = field.into();
257
258        assert_eq!(rc_field.get_name(), "num");
259        assert_eq!(rc_field.meta, DataType::Digit);
260        assert_eq!(*rc_field.value, 42);
261    }
262
263    #[test]
264    fn test_field_to_arc() {
265        let field: Field<String> = Field::new(DataType::Chars, "msg", String::from("hello"));
266        let arc_field: Field<Arc<String>> = field.into();
267
268        assert_eq!(arc_field.get_name(), "msg");
269        assert_eq!(arc_field.meta, DataType::Chars);
270        assert_eq!(*arc_field.value, "hello");
271    }
272
273    // ========== Display tests ==========
274
275    #[test]
276    fn test_field_display() {
277        let field: Field<i64> = Field::new(DataType::Digit, "count", 42);
278        let display = format!("{}", field);
279        assert!(display.contains("digit"));
280        assert!(display.contains("42"));
281    }
282
283    #[test]
284    fn test_field_display_chars() {
285        let field: Field<String> = Field::new(DataType::Chars, "msg", String::from("hello"));
286        let display = format!("{}", field);
287        assert!(display.contains("chars"));
288        assert!(display.contains("hello"));
289    }
290
291    // ========== LevelFormatAble tests ==========
292
293    #[test]
294    fn test_level_format_able() {
295        let field: Field<i64> = Field::new(DataType::Digit, "level_test", 123);
296        let mut output = String::new();
297        use std::fmt::Write;
298        // Use a simple formatter wrapper
299        let _ = write!(output, "{}", field);
300        assert!(output.contains("digit"));
301        assert!(output.contains("123"));
302    }
303
304    // ========== Clone and PartialEq tests ==========
305
306    #[test]
307    fn test_field_clone() {
308        let field: Field<i64> = Field::new(DataType::Digit, "num", 42);
309        let cloned = field.clone();
310
311        assert_eq!(field, cloned);
312        assert_eq!(cloned.get_name(), "num");
313        assert_eq!(cloned.value, 42);
314    }
315
316    #[test]
317    fn test_field_partial_eq() {
318        let field1: Field<i64> = Field::new(DataType::Digit, "num", 42);
319        let field2: Field<i64> = Field::new(DataType::Digit, "num", 42);
320        let field3: Field<i64> = Field::new(DataType::Digit, "num", 99);
321
322        assert_eq!(field1, field2);
323        assert_ne!(field1, field3);
324    }
325
326    // ========== Serde tests ==========
327
328    #[test]
329    fn test_field_serde_roundtrip() {
330        let field: Field<i64> = Field::new(DataType::Digit, "serde_test", 123);
331        let json = serde_json::to_string(&field).unwrap();
332        let parsed: Field<i64> = serde_json::from_str(&json).unwrap();
333
334        assert_eq!(field, parsed);
335    }
336
337    #[test]
338    fn test_field_serde_with_string() {
339        let field: Field<String> = Field::new(DataType::Chars, "msg", String::from("test"));
340        let json = serde_json::to_string(&field).unwrap();
341        let parsed: Field<String> = serde_json::from_str(&json).unwrap();
342
343        assert_eq!(field.name, parsed.name);
344        assert_eq!(field.value, parsed.value);
345    }
346}