wp_model_core/model/data/
maker.rs

1use crate::model::{
2    DataType, DateTimeValue, DomainT, EmailT, FNameStr, FValueStr, HexT, IdCardT, IgnoreT, Maker,
3    MobilePhoneT, UrlValue, Value,
4    types::value::{ObjectValue, SymbolValue},
5};
6use smol_str::SmolStr;
7use std::net::IpAddr;
8
9use super::Field;
10
11impl<T> Field<T>
12where
13    T: Maker<bool>,
14{
15    pub fn from_bool<S: Into<FNameStr>>(name: S, val: bool) -> Self {
16        Self::new(DataType::Bool, name.into(), T::make(val))
17    }
18}
19
20impl<T> Field<T>
21where
22    T: Maker<FValueStr>,
23{
24    pub fn from_chars<N: Into<FNameStr>, V: Into<FValueStr>>(name: N, val: V) -> Self {
25        Self::new(DataType::Chars, name.into(), T::make(val.into()))
26    }
27}
28impl<T> Field<T>
29where
30    T: Maker<Value>,
31{
32    pub fn from_symbol<N: Into<FNameStr>, V: Into<SmolStr>>(name: N, val: V) -> Self {
33        let value = SymbolValue::from(val.into());
34        Self::new(DataType::Symbol, name.into(), T::make(value.into()))
35    }
36}
37
38impl<T> Field<T>
39where
40    T: Maker<i64>,
41{
42    pub fn from_digit<S: Into<FNameStr>>(name: S, val: i64) -> Self {
43        Self::new(DataType::Digit, name.into(), T::make(val))
44    }
45}
46impl<T> Field<T>
47where
48    T: Maker<HexT>,
49{
50    pub fn from_hex<S: Into<FNameStr>>(name: S, val: HexT) -> Self {
51        Self::new(DataType::Hex, name.into(), T::make(val))
52    }
53}
54impl<T> Field<T>
55where
56    T: Maker<f64>,
57{
58    pub fn from_float<S: Into<FNameStr>>(name: S, val: f64) -> Self {
59        Self::new(DataType::Float, name.into(), T::make(val))
60    }
61}
62impl<T> Field<T>
63where
64    T: Maker<IpAddr>,
65{
66    pub fn from_ip<S: Into<FNameStr>>(name: S, ip: IpAddr) -> Self {
67        Self::new(DataType::IP, name.into(), T::make(ip))
68    }
69}
70
71impl<T> Field<T>
72where
73    T: Maker<DomainT>,
74{
75    pub fn from_domain<S: Into<FNameStr>, V: Into<SmolStr>>(name: S, domain: V) -> Self {
76        Self::new(
77            DataType::Domain,
78            name.into(),
79            T::make(DomainT(domain.into())),
80        )
81    }
82}
83
84impl<T> Field<T>
85where
86    T: Maker<UrlValue>,
87{
88    pub fn from_url<S: Into<FNameStr>, V: Into<SmolStr>>(name: S, url: V) -> Self {
89        Self::new(DataType::Url, name.into(), T::make(UrlValue(url.into())))
90    }
91}
92
93impl<T> Field<T>
94where
95    T: Maker<EmailT>,
96{
97    pub fn from_email<S: Into<FNameStr>, V: Into<SmolStr>>(name: S, email: V) -> Self {
98        Self::new(DataType::Email, name.into(), T::make(EmailT(email.into())))
99    }
100}
101
102impl<T> Field<T>
103where
104    T: Maker<IdCardT>,
105{
106    pub fn from_id_card<S: Into<FNameStr>, V: Into<SmolStr>>(name: S, id_card: V) -> Self {
107        Self::new(
108            DataType::IdCard,
109            name.into(),
110            T::make(IdCardT(id_card.into())),
111        )
112    }
113}
114
115impl<T> Field<T>
116where
117    T: Maker<MobilePhoneT>,
118{
119    pub fn from_mobile_phone<S: Into<FNameStr>, V: Into<SmolStr>>(
120        name: S,
121        mobile_phone: V,
122    ) -> Self {
123        Self::new(
124            DataType::MobilePhone,
125            name.into(),
126            T::make(MobilePhoneT(mobile_phone.into())),
127        )
128    }
129}
130
131impl<T> Field<T>
132where
133    T: Maker<IgnoreT>,
134{
135    pub fn from_ignore<S: Into<FNameStr>>(name: S) -> Self {
136        Self::new(DataType::Ignore, name.into(), T::make(IgnoreT::default()))
137    }
138}
139
140impl<T> Field<T>
141where
142    T: Maker<DateTimeValue>,
143{
144    pub fn from_time<S: Into<FNameStr>>(name: S, val: DateTimeValue) -> Self {
145        Self::new(DataType::Time, name.into(), T::make(val))
146    }
147}
148impl<T> Field<T>
149where
150    T: Maker<Vec<Field<Value>>>,
151{
152    pub fn from_arr<S: Into<FNameStr>>(name: S, val: Vec<Field<Value>>) -> Self {
153        if let Some(f) = val.first() {
154            let meta = f.get_meta().to_string();
155            Self::new(DataType::Array(meta), name.into(), T::make(val))
156        } else {
157            Self::new(DataType::Array("auto".into()), name.into(), T::make(val))
158            //unreachable!("arr is empty");
159        }
160    }
161}
162
163impl<T> Field<T>
164where
165    T: Maker<ObjectValue>,
166{
167    pub fn from_obj<S: Into<FNameStr>>(name: S, val: ObjectValue) -> Self {
168        Self::new(DataType::Obj, name.into(), T::make(val))
169    }
170}
171
172impl Value {
173    pub fn tag(&self) -> &str {
174        match self {
175            Value::Null => "Null",
176            Value::Bool(_) => "Bool",
177            Value::Chars(_) => "Chars",
178            Value::Symbol(_) => "Symbol",
179            Value::Digit(_) => "Digit",
180            Value::Time(_) => "Time",
181            Value::Hex(_) => "Hex",
182            Value::Float(_) => "Float",
183            Value::IpNet(_) => "IpNet",
184            Value::IpAddr(_) => "IpAddr",
185            Value::Ignore(_) => "Ignore",
186            Value::Obj(_) => "Map",
187            Value::Array(_) => "Array",
188            Value::Domain(_) => "Domain",
189            Value::Url(_) => "Url",
190            Value::Email(_) => "Email",
191            Value::IdCard(_) => "IdCard",
192            Value::MobilePhone(_) => "MobilePhone",
193        }
194    }
195
196    pub fn is_empty(&self) -> bool {
197        match self {
198            Value::Time(_)
199            | Value::IpNet(_)
200            | Value::IpAddr(_)
201            | Value::Float(_)
202            | Value::Digit(_)
203            | Value::Bool(_)
204            | Value::Hex(_) => false,
205            Value::Domain(v) => v.0.is_empty(),
206            Value::Url(v) => v.0.is_empty(),
207            Value::Email(v) => v.0.is_empty(),
208            Value::IdCard(v) => v.0.is_empty(),
209            Value::MobilePhone(v) => v.0.is_empty(),
210            Value::Chars(v) => v.is_empty(),
211            Value::Obj(v) => v.is_empty(),
212            Value::Array(v) => v.is_empty(),
213            Value::Symbol(v) => v.is_empty(),
214            Value::Ignore(_) => true,
215            Value::Null => true,
216        }
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223    use crate::model::{DataField, FValueStr};
224    use chrono::NaiveDateTime;
225    use smol_str::SmolStr;
226    use std::net::{IpAddr, Ipv4Addr};
227
228    // ========== Field factory method tests ==========
229
230    #[test]
231    fn test_field_from_bool() {
232        let field: DataField = Field::from_bool("is_active", true);
233        assert_eq!(field.get_name(), "is_active");
234        assert_eq!(field.meta, DataType::Bool);
235        assert_eq!(field.value, Value::Bool(true));
236    }
237
238    #[test]
239    fn test_field_from_chars() {
240        let field: DataField = Field::from_chars("message", "hello");
241        assert_eq!(field.get_name(), "message");
242        assert_eq!(field.meta, DataType::Chars);
243        assert_eq!(field.value, Value::Chars(FValueStr::from("hello")));
244    }
245
246    #[test]
247    fn test_field_from_symbol() {
248        let field: DataField = Field::from_symbol("status", "OK");
249        assert_eq!(field.get_name(), "status");
250        assert_eq!(field.meta, DataType::Symbol);
251        assert_eq!(field.value, Value::Symbol(SmolStr::from("OK")));
252    }
253
254    #[test]
255    fn test_field_from_digit() {
256        let field: DataField = Field::from_digit("count", 42);
257        assert_eq!(field.get_name(), "count");
258        assert_eq!(field.meta, DataType::Digit);
259        assert_eq!(field.value, Value::Digit(42));
260    }
261
262    #[test]
263    fn test_field_from_float() {
264        let field: DataField = Field::from_float("ratio", 2.14);
265        assert_eq!(field.get_name(), "ratio");
266        assert_eq!(field.meta, DataType::Float);
267        assert_eq!(field.value, Value::Float(2.14));
268    }
269
270    #[test]
271    fn test_field_from_hex() {
272        let field: DataField = Field::from_hex("color", HexT(0xFF00FF));
273        assert_eq!(field.get_name(), "color");
274        assert_eq!(field.meta, DataType::Hex);
275        assert_eq!(field.value, Value::Hex(HexT(0xFF00FF)));
276    }
277
278    #[test]
279    fn test_field_from_ip() {
280        let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
281        let field: DataField = Field::from_ip("src_ip", ip);
282        assert_eq!(field.get_name(), "src_ip");
283        assert_eq!(field.meta, DataType::IP);
284        assert_eq!(field.value, Value::IpAddr(ip));
285    }
286
287    #[test]
288    fn test_field_from_domain() {
289        let field: DataField = Field::from_domain("host", "example.com");
290        assert_eq!(field.get_name(), "host");
291        assert_eq!(field.meta, DataType::Domain);
292        assert_eq!(field.value, Value::Domain(DomainT("example.com".into())));
293    }
294
295    #[test]
296    fn test_field_from_url() {
297        let field: DataField = Field::from_url("link", "https://example.com");
298        assert_eq!(field.get_name(), "link");
299        assert_eq!(field.meta, DataType::Url);
300        assert_eq!(
301            field.value,
302            Value::Url(UrlValue("https://example.com".into()))
303        );
304    }
305
306    #[test]
307    fn test_field_from_email() {
308        let field: DataField = Field::from_email("contact", "test@example.com");
309        assert_eq!(field.get_name(), "contact");
310        assert_eq!(field.meta, DataType::Email);
311        assert_eq!(field.value, Value::Email(EmailT("test@example.com".into())));
312    }
313
314    #[test]
315    fn test_field_from_id_card() {
316        let field: DataField = Field::from_id_card("id", "123456789");
317        assert_eq!(field.get_name(), "id");
318        assert_eq!(field.meta, DataType::IdCard);
319        assert_eq!(field.value, Value::IdCard(IdCardT("123456789".into())));
320    }
321
322    #[test]
323    fn test_field_from_mobile_phone() {
324        let field: DataField = Field::from_mobile_phone("phone", "13800138000");
325        assert_eq!(field.get_name(), "phone");
326        assert_eq!(field.meta, DataType::MobilePhone);
327        assert_eq!(
328            field.value,
329            Value::MobilePhone(MobilePhoneT("13800138000".into()))
330        );
331    }
332
333    #[test]
334    fn test_field_from_ignore() {
335        let field: DataField = Field::from_ignore("unused");
336        assert_eq!(field.get_name(), "unused");
337        assert_eq!(field.meta, DataType::Ignore);
338        assert_eq!(field.value, Value::Ignore(IgnoreT::default()));
339    }
340
341    #[test]
342    fn test_field_from_time() {
343        let dt = NaiveDateTime::parse_from_str("2024-01-15 10:30:00", "%Y-%m-%d %H:%M:%S").unwrap();
344        let field: DataField = Field::from_time("timestamp", dt);
345        assert_eq!(field.get_name(), "timestamp");
346        assert_eq!(field.meta, DataType::Time);
347        assert_eq!(field.value, Value::Time(dt));
348    }
349
350    #[test]
351    fn test_field_from_arr_with_elements() {
352        let arr = vec![Field::from_digit("item", 1), Field::from_digit("item", 2)];
353        let field: DataField = Field::from_arr("numbers", arr);
354        assert_eq!(field.get_name(), "numbers");
355        assert_eq!(field.meta, DataType::Array("digit".into()));
356    }
357
358    #[test]
359    fn test_field_from_arr_empty() {
360        let arr: Vec<DataField> = vec![];
361        let field: DataField = Field::from_arr("empty", arr);
362        assert_eq!(field.get_name(), "empty");
363        assert_eq!(field.meta, DataType::Array("auto".into()));
364    }
365
366    #[test]
367    fn test_field_from_obj() {
368        let obj = ObjectValue::new();
369        let field: DataField = Field::from_obj("data", obj.clone());
370        assert_eq!(field.get_name(), "data");
371        assert_eq!(field.meta, DataType::Obj);
372        assert_eq!(field.value, Value::Obj(obj));
373    }
374
375    // ========== Value::tag() tests ==========
376
377    #[test]
378    fn test_value_tag() {
379        assert_eq!(Value::Null.tag(), "Null");
380        assert_eq!(Value::Bool(true).tag(), "Bool");
381        assert_eq!(Value::Chars(FValueStr::from("x")).tag(), "Chars");
382        assert_eq!(Value::Symbol(SmolStr::from("x")).tag(), "Symbol");
383        assert_eq!(Value::Digit(1).tag(), "Digit");
384        assert_eq!(Value::Float(1.0).tag(), "Float");
385        assert_eq!(Value::Hex(HexT(0)).tag(), "Hex");
386        assert_eq!(Value::Ignore(IgnoreT::default()).tag(), "Ignore");
387        assert_eq!(Value::Obj(ObjectValue::new()).tag(), "Map");
388        assert_eq!(Value::Array(vec![]).tag(), "Array");
389        assert_eq!(Value::Domain(DomainT("x".into())).tag(), "Domain");
390        assert_eq!(Value::Url(UrlValue("x".into())).tag(), "Url");
391        assert_eq!(Value::Email(EmailT("x".into())).tag(), "Email");
392        assert_eq!(Value::IdCard(IdCardT("x".into())).tag(), "IdCard");
393        assert_eq!(
394            Value::MobilePhone(MobilePhoneT("x".into())).tag(),
395            "MobilePhone"
396        );
397    }
398
399    #[test]
400    fn test_value_tag_ip() {
401        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
402        assert_eq!(Value::IpAddr(ip).tag(), "IpAddr");
403    }
404
405    // ========== Value::is_empty() tests ==========
406
407    #[test]
408    fn test_is_empty_always_false() {
409        // These types are never considered empty
410        assert!(!Value::Bool(false).is_empty());
411        assert!(!Value::Digit(0).is_empty());
412        assert!(!Value::Float(0.0).is_empty());
413        assert!(!Value::Hex(HexT(0)).is_empty());
414    }
415
416    #[test]
417    fn test_is_empty_always_true() {
418        assert!(Value::Null.is_empty());
419        assert!(Value::Ignore(IgnoreT::default()).is_empty());
420    }
421
422    #[test]
423    fn test_is_empty_string_types() {
424        // Empty strings
425        assert!(Value::Chars(FValueStr::from("")).is_empty());
426        assert!(Value::Symbol(SmolStr::from("")).is_empty());
427        assert!(Value::Domain(DomainT("".into())).is_empty());
428        assert!(Value::Url(UrlValue("".into())).is_empty());
429        assert!(Value::Email(EmailT("".into())).is_empty());
430        assert!(Value::IdCard(IdCardT("".into())).is_empty());
431        assert!(Value::MobilePhone(MobilePhoneT("".into())).is_empty());
432
433        // Non-empty strings
434        assert!(!Value::Chars(FValueStr::from("x")).is_empty());
435        assert!(!Value::Symbol(SmolStr::from("x")).is_empty());
436        assert!(!Value::Domain(DomainT("x".into())).is_empty());
437        assert!(!Value::Url(UrlValue("x".into())).is_empty());
438        assert!(!Value::Email(EmailT("x".into())).is_empty());
439        assert!(!Value::IdCard(IdCardT("x".into())).is_empty());
440        assert!(!Value::MobilePhone(MobilePhoneT("x".into())).is_empty());
441    }
442
443    #[test]
444    fn test_is_empty_collections() {
445        // Empty
446        assert!(Value::Array(vec![]).is_empty());
447        assert!(Value::Obj(ObjectValue::new()).is_empty());
448
449        // Non-empty
450        let arr = vec![Field::from_digit("x", 1)];
451        assert!(!Value::Array(arr).is_empty());
452    }
453}