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(v.get_name()),
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
288// ============================================================================
289// 新 trait 实现:ValueFormatter + RecordFormatter
290// ============================================================================
291
292#[allow(clippy::items_after_test_module)]
293impl ValueFormatter for KeyValue {
294    type Output = String;
295
296    fn format_value(&self, value: &wp_model_core::model::Value) -> String {
297        use wp_model_core::model::Value;
298        match value {
299            Value::Null => String::new(),
300            Value::Bool(v) => if *v { "true" } else { "false" }.to_string(),
301            Value::Chars(v) => self.format_string_value(v),
302            Value::Digit(v) => v.to_string(),
303            Value::Float(v) => v.to_string(),
304            Value::IpAddr(v) => v.to_string(),
305            Value::Time(v) => v.to_string(),
306            Value::Obj(obj) => {
307                let mut output = String::new();
308                output.push('{');
309                for (i, (_k, field)) in obj.iter().enumerate() {
310                    if i > 0 {
311                        output.push_str(&self.pair_separator);
312                    }
313                    write!(
314                        output,
315                        "{}{}{}",
316                        self.format_string_value(field.get_name()),
317                        self.key_value_separator,
318                        self.format_value(field.get_value())
319                    )
320                    .unwrap();
321                }
322                output.push('}');
323                output
324            }
325            Value::Array(arr) => {
326                let mut output = String::new();
327                output.push('[');
328                for (i, field) in arr.iter().enumerate() {
329                    if i > 0 {
330                        output.push_str(&self.pair_separator);
331                    }
332                    output.push_str(&self.format_value(field.get_value()));
333                }
334                output.push(']');
335                output
336            }
337            _ => value.to_string(),
338        }
339    }
340}
341
342impl RecordFormatter for KeyValue {
343    fn fmt_field(&self, field: &FieldStorage) -> String {
344        format!(
345            "{}{}{}",
346            field.get_name(),
347            self.key_value_separator,
348            self.format_value(field.get_value())
349        )
350    }
351
352    fn fmt_record(&self, record: &DataRecord) -> String {
353        record
354            .items
355            .iter()
356            .filter(|f| *f.get_meta() != DataType::Ignore)
357            .map(|field| self.fmt_field(field))
358            .collect::<Vec<_>>()
359            .join(&self.pair_separator)
360    }
361}