Skip to main content

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::BigUint(_) => "BigUint",
181            Value::Time(_) => "Time",
182            Value::Hex(_) => "Hex",
183            Value::Float(_) => "Float",
184            Value::IpNet(_) => "IpNet",
185            Value::IpAddr(_) => "IpAddr",
186            Value::Ignore(_) => "Ignore",
187            Value::Obj(_) => "Map",
188            Value::Array(_) => "Array",
189            Value::Domain(_) => "Domain",
190            Value::Url(_) => "Url",
191            Value::Email(_) => "Email",
192            Value::IdCard(_) => "IdCard",
193            Value::MobilePhone(_) => "MobilePhone",
194        }
195    }
196
197    pub fn is_empty(&self) -> bool {
198        match self {
199            Value::Time(_)
200            | Value::IpNet(_)
201            | Value::IpAddr(_)
202            | Value::Float(_)
203            | Value::Digit(_)
204            | Value::BigUint(_)
205            | Value::Bool(_)
206            | Value::Hex(_) => false,
207            Value::Domain(v) => v.0.is_empty(),
208            Value::Url(v) => v.0.is_empty(),
209            Value::Email(v) => v.0.is_empty(),
210            Value::IdCard(v) => v.0.is_empty(),
211            Value::MobilePhone(v) => v.0.is_empty(),
212            Value::Chars(v) => v.is_empty(),
213            Value::Obj(v) => v.is_empty(),
214            Value::Array(v) => v.is_empty(),
215            Value::Symbol(v) => v.is_empty(),
216            Value::Ignore(_) => true,
217            Value::Null => true,
218        }
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use crate::model::{DataField, FValueStr};
226    use chrono::NaiveDateTime;
227    use smol_str::SmolStr;
228    use std::net::{IpAddr, Ipv4Addr};
229
230    // ========== Field factory method tests ==========
231
232    #[test]
233    fn test_field_from_bool() {
234        let field: DataField = Field::from_bool("is_active", true);
235        assert_eq!(field.get_name(), "is_active");
236        assert_eq!(field.meta, DataType::Bool);
237        assert_eq!(field.value, Value::Bool(true));
238    }
239
240    #[test]
241    fn test_field_from_chars() {
242        let field: DataField = Field::from_chars("message", "hello");
243        assert_eq!(field.get_name(), "message");
244        assert_eq!(field.meta, DataType::Chars);
245        assert_eq!(field.value, Value::Chars(FValueStr::from("hello")));
246    }
247
248    #[test]
249    fn test_field_from_symbol() {
250        let field: DataField = Field::from_symbol("status", "OK");
251        assert_eq!(field.get_name(), "status");
252        assert_eq!(field.meta, DataType::Symbol);
253        assert_eq!(field.value, Value::Symbol(SmolStr::from("OK")));
254    }
255
256    #[test]
257    fn test_field_from_digit() {
258        let field: DataField = Field::from_digit("count", 42);
259        assert_eq!(field.get_name(), "count");
260        assert_eq!(field.meta, DataType::Digit);
261        assert_eq!(field.value, Value::Digit(42));
262    }
263
264    #[test]
265    fn test_field_from_float() {
266        let field: DataField = Field::from_float("ratio", 2.14);
267        assert_eq!(field.get_name(), "ratio");
268        assert_eq!(field.meta, DataType::Float);
269        assert_eq!(field.value, Value::Float(2.14));
270    }
271
272    #[test]
273    fn test_field_from_hex() {
274        let field: DataField = Field::from_hex("color", HexT(0xFF00FF));
275        assert_eq!(field.get_name(), "color");
276        assert_eq!(field.meta, DataType::Hex);
277        assert_eq!(field.value, Value::Hex(HexT(0xFF00FF)));
278    }
279
280    #[test]
281    fn test_field_from_ip() {
282        let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
283        let field: DataField = Field::from_ip("src_ip", ip);
284        assert_eq!(field.get_name(), "src_ip");
285        assert_eq!(field.meta, DataType::IP);
286        assert_eq!(field.value, Value::IpAddr(ip));
287    }
288
289    #[test]
290    fn test_field_from_domain() {
291        let field: DataField = Field::from_domain("host", "example.com");
292        assert_eq!(field.get_name(), "host");
293        assert_eq!(field.meta, DataType::Domain);
294        assert_eq!(field.value, Value::Domain(DomainT("example.com".into())));
295    }
296
297    #[test]
298    fn test_field_from_url() {
299        let field: DataField = Field::from_url("link", "https://example.com");
300        assert_eq!(field.get_name(), "link");
301        assert_eq!(field.meta, DataType::Url);
302        assert_eq!(
303            field.value,
304            Value::Url(UrlValue("https://example.com".into()))
305        );
306    }
307
308    #[test]
309    fn test_field_from_email() {
310        let field: DataField = Field::from_email("contact", "test@example.com");
311        assert_eq!(field.get_name(), "contact");
312        assert_eq!(field.meta, DataType::Email);
313        assert_eq!(field.value, Value::Email(EmailT("test@example.com".into())));
314    }
315
316    #[test]
317    fn test_field_from_id_card() {
318        let field: DataField = Field::from_id_card("id", "123456789");
319        assert_eq!(field.get_name(), "id");
320        assert_eq!(field.meta, DataType::IdCard);
321        assert_eq!(field.value, Value::IdCard(IdCardT("123456789".into())));
322    }
323
324    #[test]
325    fn test_field_from_mobile_phone() {
326        let field: DataField = Field::from_mobile_phone("phone", "13800138000");
327        assert_eq!(field.get_name(), "phone");
328        assert_eq!(field.meta, DataType::MobilePhone);
329        assert_eq!(
330            field.value,
331            Value::MobilePhone(MobilePhoneT("13800138000".into()))
332        );
333    }
334
335    #[test]
336    fn test_field_from_ignore() {
337        let field: DataField = Field::from_ignore("unused");
338        assert_eq!(field.get_name(), "unused");
339        assert_eq!(field.meta, DataType::Ignore);
340        assert_eq!(field.value, Value::Ignore(IgnoreT::default()));
341    }
342
343    #[test]
344    fn test_field_from_time() {
345        let dt = NaiveDateTime::parse_from_str("2024-01-15 10:30:00", "%Y-%m-%d %H:%M:%S").unwrap();
346        let field: DataField = Field::from_time("timestamp", dt);
347        assert_eq!(field.get_name(), "timestamp");
348        assert_eq!(field.meta, DataType::Time);
349        assert_eq!(field.value, Value::Time(dt));
350    }
351
352    #[test]
353    fn test_field_from_arr_with_elements() {
354        let arr = vec![Field::from_digit("item", 1), Field::from_digit("item", 2)];
355        let field: DataField = Field::from_arr("numbers", arr);
356        assert_eq!(field.get_name(), "numbers");
357        assert_eq!(field.meta, DataType::Array("digit".into()));
358    }
359
360    #[test]
361    fn test_field_from_arr_empty() {
362        let arr: Vec<DataField> = vec![];
363        let field: DataField = Field::from_arr("empty", arr);
364        assert_eq!(field.get_name(), "empty");
365        assert_eq!(field.meta, DataType::Array("auto".into()));
366    }
367
368    #[test]
369    fn test_field_from_obj() {
370        let obj = ObjectValue::new();
371        let field: DataField = Field::from_obj("data", obj.clone());
372        assert_eq!(field.get_name(), "data");
373        assert_eq!(field.meta, DataType::Obj);
374        assert_eq!(field.value, Value::Obj(obj));
375    }
376
377    // ========== Value::tag() tests ==========
378
379    #[test]
380    fn test_value_tag() {
381        assert_eq!(Value::Null.tag(), "Null");
382        assert_eq!(Value::Bool(true).tag(), "Bool");
383        assert_eq!(Value::Chars(FValueStr::from("x")).tag(), "Chars");
384        assert_eq!(Value::Symbol(SmolStr::from("x")).tag(), "Symbol");
385        assert_eq!(Value::Digit(1).tag(), "Digit");
386        assert_eq!(Value::Float(1.0).tag(), "Float");
387        assert_eq!(Value::Hex(HexT(0)).tag(), "Hex");
388        assert_eq!(Value::Ignore(IgnoreT::default()).tag(), "Ignore");
389        assert_eq!(Value::Obj(ObjectValue::new()).tag(), "Map");
390        assert_eq!(Value::Array(vec![]).tag(), "Array");
391        assert_eq!(Value::Domain(DomainT("x".into())).tag(), "Domain");
392        assert_eq!(Value::Url(UrlValue("x".into())).tag(), "Url");
393        assert_eq!(Value::Email(EmailT("x".into())).tag(), "Email");
394        assert_eq!(Value::IdCard(IdCardT("x".into())).tag(), "IdCard");
395        assert_eq!(
396            Value::MobilePhone(MobilePhoneT("x".into())).tag(),
397            "MobilePhone"
398        );
399    }
400
401    #[test]
402    fn test_value_tag_ip() {
403        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
404        assert_eq!(Value::IpAddr(ip).tag(), "IpAddr");
405    }
406
407    // ========== Value::is_empty() tests ==========
408
409    #[test]
410    fn test_is_empty_always_false() {
411        // These types are never considered empty
412        assert!(!Value::Bool(false).is_empty());
413        assert!(!Value::Digit(0).is_empty());
414        assert!(!Value::Float(0.0).is_empty());
415        assert!(!Value::Hex(HexT(0)).is_empty());
416    }
417
418    #[test]
419    fn test_is_empty_always_true() {
420        assert!(Value::Null.is_empty());
421        assert!(Value::Ignore(IgnoreT::default()).is_empty());
422    }
423
424    #[test]
425    fn test_is_empty_string_types() {
426        // Empty strings
427        assert!(Value::Chars(FValueStr::from("")).is_empty());
428        assert!(Value::Symbol(SmolStr::from("")).is_empty());
429        assert!(Value::Domain(DomainT("".into())).is_empty());
430        assert!(Value::Url(UrlValue("".into())).is_empty());
431        assert!(Value::Email(EmailT("".into())).is_empty());
432        assert!(Value::IdCard(IdCardT("".into())).is_empty());
433        assert!(Value::MobilePhone(MobilePhoneT("".into())).is_empty());
434
435        // Non-empty strings
436        assert!(!Value::Chars(FValueStr::from("x")).is_empty());
437        assert!(!Value::Symbol(SmolStr::from("x")).is_empty());
438        assert!(!Value::Domain(DomainT("x".into())).is_empty());
439        assert!(!Value::Url(UrlValue("x".into())).is_empty());
440        assert!(!Value::Email(EmailT("x".into())).is_empty());
441        assert!(!Value::IdCard(IdCardT("x".into())).is_empty());
442        assert!(!Value::MobilePhone(MobilePhoneT("x".into())).is_empty());
443    }
444
445    #[test]
446    fn test_is_empty_collections() {
447        // Empty
448        assert!(Value::Array(vec![]).is_empty());
449        assert!(Value::Obj(ObjectValue::new()).is_empty());
450
451        // Non-empty
452        let arr = vec![Field::from_digit("x", 1)];
453        assert!(!Value::Array(arr.into_iter().collect()).is_empty());
454    }
455}