1use crate::formatter::DataFormat;
2use std::fmt::Write;
3use wp_model_core::model::{DataField, DataRecord, DataType, types::value::ObjectValue};
4
5pub struct KeyValue {
6 pair_separator: String,
7 key_value_separator: String,
8 quote_strings: bool,
9}
10
11impl Default for KeyValue {
12 fn default() -> Self {
13 Self {
14 pair_separator: ", ".to_string(),
15 key_value_separator: ": ".to_string(),
16 quote_strings: true,
17 }
18 }
19}
20
21impl KeyValue {
22 pub fn new() -> Self {
23 Self::default()
24 }
25 pub fn with_pair_separator(mut self, s: impl Into<String>) -> Self {
26 self.pair_separator = s.into();
27 self
28 }
29 pub fn with_key_value_separator(mut self, s: impl Into<String>) -> Self {
30 self.key_value_separator = s.into();
31 self
32 }
33 pub fn with_quote_strings(mut self, quote: bool) -> Self {
34 self.quote_strings = quote;
35 self
36 }
37
38 fn format_string_value(&self, value: &str) -> String {
39 if self.quote_strings {
40 format!("\"{}\"", value.replace('\"', "\\\""))
41 } else {
42 value.to_string()
43 }
44 }
45}
46
47impl DataFormat for KeyValue {
48 type Output = String;
49
50 fn format_null(&self) -> String {
51 String::new()
52 }
53 fn format_bool(&self, v: &bool) -> String {
54 if *v { "true".into() } else { "false".into() }
55 }
56 fn format_string(&self, v: &str) -> String {
57 self.format_string_value(v)
58 }
59 fn format_i64(&self, v: &i64) -> String {
60 v.to_string()
61 }
62 fn format_f64(&self, v: &f64) -> String {
63 v.to_string()
64 }
65 fn format_ip(&self, v: &std::net::IpAddr) -> String {
66 v.to_string()
67 }
68 fn format_datetime(&self, v: &chrono::NaiveDateTime) -> String {
69 v.to_string()
70 }
71
72 fn format_object(&self, value: &ObjectValue) -> String {
73 let mut output = String::new();
74 output.push('{');
75 for (i, (k, v)) in value.iter().enumerate() {
76 if i > 0 {
77 output.push_str(&self.pair_separator);
78 }
79 write!(
80 output,
81 "{}{}{}",
82 self.format_string(k),
83 self.key_value_separator,
84 self.fmt_value(v.get_value())
85 )
86 .unwrap();
87 }
88 output.push('}');
89 output
90 }
91
92 fn format_array(&self, value: &[DataField]) -> String {
93 let mut output = String::new();
94 output.push('[');
95 for (i, field) in value.iter().enumerate() {
96 if i > 0 {
97 output.push_str(&self.pair_separator);
98 }
99 output.push_str(&self.fmt_value(field.get_value()));
100 }
101 output.push(']');
102 output
103 }
104
105 fn format_field(&self, field: &DataField) -> String {
106 format!(
107 "{}{}{}",
108 field.get_name(),
109 self.key_value_separator,
110 self.fmt_value(field.get_value())
111 )
112 }
113
114 fn format_record(&self, record: &DataRecord) -> String {
115 record
116 .items
117 .iter()
118 .filter(|f| *f.get_meta() != DataType::Ignore)
119 .map(|field| self.format_field(field))
120 .collect::<Vec<_>>()
121 .join(&self.pair_separator)
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use std::net::IpAddr;
129 use std::str::FromStr;
130
131 #[test]
132 fn test_kv_default() {
133 let kv = KeyValue::default();
134 assert_eq!(kv.pair_separator, ", ");
135 assert_eq!(kv.key_value_separator, ": ");
136 assert!(kv.quote_strings);
137 }
138
139 #[test]
140 fn test_kv_new() {
141 let kv = KeyValue::new();
142 assert_eq!(kv.pair_separator, ", ");
143 }
144
145 #[test]
146 fn test_kv_builder_pattern() {
147 let kv = KeyValue::new()
148 .with_pair_separator("; ")
149 .with_key_value_separator("=")
150 .with_quote_strings(false);
151 assert_eq!(kv.pair_separator, "; ");
152 assert_eq!(kv.key_value_separator, "=");
153 assert!(!kv.quote_strings);
154 }
155
156 #[test]
157 fn test_format_null() {
158 let kv = KeyValue::default();
159 assert_eq!(kv.format_null(), "");
160 }
161
162 #[test]
163 fn test_format_bool() {
164 let kv = KeyValue::default();
165 assert_eq!(kv.format_bool(&true), "true");
166 assert_eq!(kv.format_bool(&false), "false");
167 }
168
169 #[test]
170 fn test_format_string_with_quotes() {
171 let kv = KeyValue::default();
172 assert_eq!(kv.format_string("hello"), "\"hello\"");
173 assert_eq!(kv.format_string("world"), "\"world\"");
174 }
175
176 #[test]
177 fn test_format_string_without_quotes() {
178 let kv = KeyValue::new().with_quote_strings(false);
179 assert_eq!(kv.format_string("hello"), "hello");
180 }
181
182 #[test]
183 fn test_format_string_escape_quotes() {
184 let kv = KeyValue::default();
185 assert_eq!(kv.format_string("say \"hi\""), "\"say \\\"hi\\\"\"");
186 }
187
188 #[test]
189 fn test_format_i64() {
190 let kv = KeyValue::default();
191 assert_eq!(kv.format_i64(&0), "0");
192 assert_eq!(kv.format_i64(&42), "42");
193 assert_eq!(kv.format_i64(&-100), "-100");
194 }
195
196 #[test]
197 fn test_format_f64() {
198 let kv = KeyValue::default();
199 assert_eq!(kv.format_f64(&3.24), "3.24");
200 assert_eq!(kv.format_f64(&0.0), "0");
201 }
202
203 #[test]
204 fn test_format_ip() {
205 let kv = KeyValue::default();
206 let ip = IpAddr::from_str("192.168.1.1").unwrap();
207 assert_eq!(kv.format_ip(&ip), "192.168.1.1");
208 }
209
210 #[test]
211 fn test_format_datetime() {
212 let kv = KeyValue::default();
213 let dt = chrono::NaiveDateTime::parse_from_str("2024-01-15 10:30:45", "%Y-%m-%d %H:%M:%S")
214 .unwrap();
215 let result = kv.format_datetime(&dt);
216 assert!(result.contains("2024"));
217 }
218
219 #[test]
220 fn test_format_field() {
221 let kv = KeyValue::default();
222 let field = DataField::from_chars("name", "Alice");
223 let result = kv.format_field(&field);
224 assert_eq!(result, "name: \"Alice\"");
225 }
226
227 #[test]
228 fn test_format_field_with_custom_separator() {
229 let kv = KeyValue::new().with_key_value_separator("=");
230 let field = DataField::from_digit("age", 30);
231 let result = kv.format_field(&field);
232 assert_eq!(result, "age=30");
233 }
234
235 #[test]
236 fn test_format_record() {
237 let kv = KeyValue::default();
238 let record = DataRecord {
239 items: vec![
240 DataField::from_chars("name", "Alice"),
241 DataField::from_digit("age", 30),
242 ],
243 };
244 let result = kv.format_record(&record);
245 assert!(result.contains("name: \"Alice\""));
246 assert!(result.contains("age: 30"));
247 assert!(result.contains(", "));
248 }
249
250 #[test]
251 fn test_format_record_custom_separators() {
252 let kv = KeyValue::new()
253 .with_pair_separator(" | ")
254 .with_key_value_separator("=")
255 .with_quote_strings(false);
256 let record = DataRecord {
257 items: vec![
258 DataField::from_chars("a", "x"),
259 DataField::from_chars("b", "y"),
260 ],
261 };
262 let result = kv.format_record(&record);
263 assert_eq!(result, "a=x | b=y");
264 }
265
266 #[test]
267 fn test_format_array() {
268 let kv = KeyValue::default();
269 let arr = vec![DataField::from_digit("", 1), DataField::from_digit("", 2)];
270 let result = kv.format_array(&arr);
271 assert!(result.starts_with('['));
272 assert!(result.ends_with(']'));
273 assert!(result.contains("1"));
274 assert!(result.contains("2"));
275 }
276}