Skip to main content

wp_data_fmt/
kv.rs

1#[allow(deprecated)]
2use crate::formatter::DataFormat;
3use crate::formatter::{RecordFormatter, ValueFormatter};
4use std::fmt::Write;
5use wp_model_core::model::{
6    DataRecord, DataType, FieldStorage, data::record::RecordItem, types::value::ObjectValue,
7};
8
9pub struct KeyValue {
10    pair_separator: String,
11    key_value_separator: String,
12    quote_strings: bool,
13}
14
15impl Default for KeyValue {
16    fn default() -> Self {
17        Self {
18            pair_separator: ", ".to_string(),
19            key_value_separator: ": ".to_string(),
20            quote_strings: true,
21        }
22    }
23}
24
25impl KeyValue {
26    pub fn new() -> Self {
27        Self::default()
28    }
29    pub fn with_pair_separator(mut self, s: impl Into<String>) -> Self {
30        self.pair_separator = s.into();
31        self
32    }
33    pub fn with_key_value_separator(mut self, s: impl Into<String>) -> Self {
34        self.key_value_separator = s.into();
35        self
36    }
37    pub fn with_quote_strings(mut self, quote: bool) -> Self {
38        self.quote_strings = quote;
39        self
40    }
41
42    fn format_string_value(&self, value: &str) -> String {
43        if self.quote_strings {
44            format!("\"{}\"", value.replace('\"', "\\\""))
45        } else {
46            value.to_string()
47        }
48    }
49}
50
51#[allow(deprecated)]
52impl DataFormat for KeyValue {
53    type Output = String;
54
55    fn format_null(&self) -> String {
56        String::new()
57    }
58    fn format_bool(&self, v: &bool) -> String {
59        if *v { "true".into() } else { "false".into() }
60    }
61    fn format_string(&self, v: &str) -> String {
62        self.format_string_value(v)
63    }
64    fn format_i64(&self, v: &i64) -> String {
65        v.to_string()
66    }
67    fn format_f64(&self, v: &f64) -> String {
68        v.to_string()
69    }
70    fn format_ip(&self, v: &std::net::IpAddr) -> String {
71        v.to_string()
72    }
73    fn format_datetime(&self, v: &chrono::NaiveDateTime) -> String {
74        v.to_string()
75    }
76
77    fn format_object(&self, value: &ObjectValue) -> String {
78        let mut output = String::new();
79        output.push('{');
80        for (i, (k, v)) in value.iter().enumerate() {
81            if i > 0 {
82                output.push_str(&self.pair_separator);
83            }
84            write!(
85                output,
86                "{}{}{}",
87                self.format_string(k),
88                self.key_value_separator,
89                self.fmt_value(v.get_value())
90            )
91            .unwrap();
92        }
93        output.push('}');
94        output
95    }
96
97    fn format_array(&self, value: &[FieldStorage]) -> String {
98        let mut output = String::new();
99        output.push('[');
100        for (i, field) in value.iter().enumerate() {
101            if i > 0 {
102                output.push_str(&self.pair_separator);
103            }
104            output.push_str(&self.fmt_value(field.get_value()));
105        }
106        output.push(']');
107        output
108    }
109
110    fn format_field(&self, field: &FieldStorage) -> String {
111        format!(
112            "{}{}{}",
113            field.get_name(),
114            self.key_value_separator,
115            self.fmt_value(field.get_value())
116        )
117    }
118
119    fn format_record(&self, record: &DataRecord) -> String {
120        record
121            .items
122            .iter()
123            .filter(|f| *f.get_meta() != DataType::Ignore)
124            .map(|field| self.format_field(field))
125            .collect::<Vec<_>>()
126            .join(&self.pair_separator)
127    }
128}
129
130#[cfg(test)]
131#[allow(deprecated)]
132mod tests {
133    use super::*;
134    use std::net::IpAddr;
135    use std::str::FromStr;
136    use wp_model_core::model::DataField;
137
138    #[test]
139    fn test_kv_default() {
140        let kv = KeyValue::default();
141        assert_eq!(kv.pair_separator, ", ");
142        assert_eq!(kv.key_value_separator, ": ");
143        assert!(kv.quote_strings);
144    }
145
146    #[test]
147    fn test_kv_new() {
148        let kv = KeyValue::new();
149        assert_eq!(kv.pair_separator, ", ");
150    }
151
152    #[test]
153    fn test_kv_builder_pattern() {
154        let kv = KeyValue::new()
155            .with_pair_separator("; ")
156            .with_key_value_separator("=")
157            .with_quote_strings(false);
158        assert_eq!(kv.pair_separator, "; ");
159        assert_eq!(kv.key_value_separator, "=");
160        assert!(!kv.quote_strings);
161    }
162
163    #[test]
164    fn test_format_null() {
165        let kv = KeyValue::default();
166        assert_eq!(kv.format_null(), "");
167    }
168
169    #[test]
170    fn test_format_bool() {
171        let kv = KeyValue::default();
172        assert_eq!(kv.format_bool(&true), "true");
173        assert_eq!(kv.format_bool(&false), "false");
174    }
175
176    #[test]
177    fn test_format_string_with_quotes() {
178        let kv = KeyValue::default();
179        assert_eq!(kv.format_string("hello"), "\"hello\"");
180        assert_eq!(kv.format_string("world"), "\"world\"");
181    }
182
183    #[test]
184    fn test_format_string_without_quotes() {
185        let kv = KeyValue::new().with_quote_strings(false);
186        assert_eq!(kv.format_string("hello"), "hello");
187    }
188
189    #[test]
190    fn test_format_string_escape_quotes() {
191        let kv = KeyValue::default();
192        assert_eq!(kv.format_string("say \"hi\""), "\"say \\\"hi\\\"\"");
193    }
194
195    #[test]
196    fn test_format_i64() {
197        let kv = KeyValue::default();
198        assert_eq!(kv.format_i64(&0), "0");
199        assert_eq!(kv.format_i64(&42), "42");
200        assert_eq!(kv.format_i64(&-100), "-100");
201    }
202
203    #[test]
204    fn test_format_f64() {
205        let kv = KeyValue::default();
206        assert_eq!(kv.format_f64(&3.24), "3.24");
207        assert_eq!(kv.format_f64(&0.0), "0");
208    }
209
210    #[test]
211    fn test_format_ip() {
212        let kv = KeyValue::default();
213        let ip = IpAddr::from_str("192.168.1.1").unwrap();
214        assert_eq!(kv.format_ip(&ip), "192.168.1.1");
215    }
216
217    #[test]
218    fn test_format_datetime() {
219        let kv = KeyValue::default();
220        let dt = chrono::NaiveDateTime::parse_from_str("2024-01-15 10:30:45", "%Y-%m-%d %H:%M:%S")
221            .unwrap();
222        let result = kv.format_datetime(&dt);
223        assert!(result.contains("2024"));
224    }
225
226    #[test]
227    fn test_format_field() {
228        let kv = KeyValue::default();
229        let field = FieldStorage::Owned(DataField::from_chars("name", "Alice"));
230        let result = kv.format_field(&field);
231        assert_eq!(result, "name: \"Alice\"");
232    }
233
234    #[test]
235    fn test_format_field_with_custom_separator() {
236        let kv = KeyValue::new().with_key_value_separator("=");
237        let field = FieldStorage::Owned(DataField::from_digit("age", 30));
238        let result = kv.format_field(&field);
239        assert_eq!(result, "age=30");
240    }
241
242    #[test]
243    fn test_format_record() {
244        let kv = KeyValue::default();
245        let record = DataRecord {
246            id: Default::default(),
247            items: vec![
248                FieldStorage::Owned(DataField::from_chars("name", "Alice")),
249                FieldStorage::Owned(DataField::from_digit("age", 30)),
250            ],
251        };
252        let result = kv.format_record(&record);
253        assert!(result.contains("name: \"Alice\""));
254        assert!(result.contains("age: 30"));
255        assert!(result.contains(", "));
256    }
257
258    #[test]
259    fn test_format_record_custom_separators() {
260        let kv = KeyValue::new()
261            .with_pair_separator(" | ")
262            .with_key_value_separator("=")
263            .with_quote_strings(false);
264        let record = DataRecord {
265            id: Default::default(),
266            items: vec![
267                FieldStorage::Owned(DataField::from_chars("a", "x")),
268                FieldStorage::Owned(DataField::from_chars("b", "y")),
269            ],
270        };
271        let result = kv.format_record(&record);
272        assert_eq!(result, "a=x | b=y");
273    }
274
275    #[test]
276    fn test_format_array() {
277        let kv = KeyValue::default();
278        let arr = vec![
279            FieldStorage::Owned(DataField::from_digit("", 1)),
280            FieldStorage::Owned(DataField::from_digit("", 2)),
281        ];
282        let result = kv.format_array(&arr);
283        assert!(result.starts_with('['));
284        assert!(result.ends_with(']'));
285        assert!(result.contains("1"));
286        assert!(result.contains("2"));
287    }
288}
289
290// ============================================================================
291// 新 trait 实现:ValueFormatter + RecordFormatter
292// ============================================================================
293
294#[allow(clippy::items_after_test_module)]
295impl ValueFormatter for KeyValue {
296    type Output = String;
297
298    fn format_value(&self, value: &wp_model_core::model::Value) -> String {
299        use wp_model_core::model::Value;
300        match value {
301            Value::Null => String::new(),
302            Value::Bool(v) => if *v { "true" } else { "false" }.to_string(),
303            Value::Chars(v) => self.format_string_value(v),
304            Value::Digit(v) => v.to_string(),
305            Value::Float(v) => v.to_string(),
306            Value::IpAddr(v) => v.to_string(),
307            Value::Time(v) => v.to_string(),
308            Value::Obj(obj) => {
309                let mut output = String::new();
310                output.push('{');
311                for (i, (k, field)) in obj.iter().enumerate() {
312                    if i > 0 {
313                        output.push_str(&self.pair_separator);
314                    }
315                    write!(
316                        output,
317                        "{}{}{}",
318                        self.format_string_value(k),
319                        self.key_value_separator,
320                        self.format_value(field.get_value())
321                    )
322                    .unwrap();
323                }
324                output.push('}');
325                output
326            }
327            Value::Array(arr) => {
328                let mut output = String::new();
329                output.push('[');
330                for (i, field) in arr.iter().enumerate() {
331                    if i > 0 {
332                        output.push_str(&self.pair_separator);
333                    }
334                    output.push_str(&self.format_value(field.get_value()));
335                }
336                output.push(']');
337                output
338            }
339            _ => value.to_string(),
340        }
341    }
342}
343
344impl RecordFormatter for KeyValue {
345    fn fmt_field(&self, field: &FieldStorage) -> String {
346        format!(
347            "{}{}{}",
348            field.get_name(),
349            self.key_value_separator,
350            self.format_value(field.get_value())
351        )
352    }
353
354    fn fmt_record(&self, record: &DataRecord) -> String {
355        record
356            .items
357            .iter()
358            .filter(|f| *f.get_meta() != DataType::Ignore)
359            .map(|field| self.fmt_field(field))
360            .collect::<Vec<_>>()
361            .join(&self.pair_separator)
362    }
363}