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#[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}