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