1use crate::formatter::DataFormat;
2use wp_model_core::model::{DataField, DataRecord, DataType, Value, types::value::ObjectValue};
3
4#[derive(Default)]
5pub struct ProtoTxt;
6
7impl ProtoTxt {
8 pub fn new() -> Self {
9 Self
10 }
11}
12
13impl DataFormat for ProtoTxt {
14 type Output = String;
15 fn format_null(&self) -> String {
16 String::new()
17 }
18 fn format_bool(&self, v: &bool) -> String {
19 v.to_string()
20 }
21 fn format_string(&self, v: &str) -> String {
22 format!("\"{}\"", v.replace('"', "\\\""))
23 }
24 fn format_i64(&self, v: &i64) -> String {
25 v.to_string()
26 }
27 fn format_f64(&self, v: &f64) -> String {
28 v.to_string()
29 }
30 fn format_ip(&self, v: &std::net::IpAddr) -> String {
31 self.format_string(&v.to_string())
32 }
33 fn format_datetime(&self, v: &chrono::NaiveDateTime) -> String {
34 self.format_string(&v.to_string())
35 }
36 fn format_object(&self, value: &ObjectValue) -> String {
37 let mut out = String::new();
38 for (k, v) in value.iter() {
39 out.push_str(&format!("{}: {}\n", k, self.fmt_value(v.get_value())));
40 }
41 out
42 }
43 fn format_array(&self, value: &[DataField]) -> String {
44 let items: Vec<String> = value.iter().map(|f| self.format_field(f)).collect();
45 format!("[{}]", items.join(", "))
46 }
47 fn format_field(&self, field: &DataField) -> String {
48 if *field.get_meta() == DataType::Ignore {
49 String::new()
50 } else {
51 match field.get_value() {
52 Value::Obj(_) | Value::Array(_) => format!(
53 "{}: {}",
54 field.get_name(),
55 self.fmt_value(field.get_value())
56 ),
57 _ => format!(
58 "{}: {}",
59 field.get_name(),
60 self.fmt_value(field.get_value())
61 ),
62 }
63 }
64 }
65 fn format_record(&self, record: &DataRecord) -> String {
66 let items = record
67 .items
68 .iter()
69 .filter(|f| *f.get_meta() != DataType::Ignore)
70 .map(|f| self.format_field(f))
71 .collect::<Vec<_>>();
72 format!("{{ {} }}", items.join(" "))
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use std::net::IpAddr;
81 use std::str::FromStr;
82
83 #[test]
84 fn test_proto_new() {
85 let proto = ProtoTxt::new();
86 assert_eq!(proto.format_null(), "");
87 }
88
89 #[test]
90 fn test_proto_default() {
91 let proto = ProtoTxt;
92 assert_eq!(proto.format_null(), "");
93 }
94
95 #[test]
96 fn test_format_null() {
97 let proto = ProtoTxt;
98 assert_eq!(proto.format_null(), "");
99 }
100
101 #[test]
102 fn test_format_bool() {
103 let proto = ProtoTxt;
104 assert_eq!(proto.format_bool(&true), "true");
105 assert_eq!(proto.format_bool(&false), "false");
106 }
107
108 #[test]
109 fn test_format_string() {
110 let proto = ProtoTxt;
111 assert_eq!(proto.format_string("hello"), "\"hello\"");
112 assert_eq!(proto.format_string(""), "\"\"");
113 }
114
115 #[test]
116 fn test_format_string_escape_quotes() {
117 let proto = ProtoTxt;
118 assert_eq!(proto.format_string("say \"hi\""), "\"say \\\"hi\\\"\"");
119 }
120
121 #[test]
122 fn test_format_i64() {
123 let proto = ProtoTxt;
124 assert_eq!(proto.format_i64(&0), "0");
125 assert_eq!(proto.format_i64(&42), "42");
126 assert_eq!(proto.format_i64(&-100), "-100");
127 }
128
129 #[test]
130 fn test_format_f64() {
131 let proto = ProtoTxt;
132 assert_eq!(proto.format_f64(&3.24), "3.24");
133 assert_eq!(proto.format_f64(&0.0), "0");
134 }
135
136 #[test]
137 fn test_format_ip() {
138 let proto = ProtoTxt;
139 let ip = IpAddr::from_str("192.168.1.1").unwrap();
140 assert_eq!(proto.format_ip(&ip), "\"192.168.1.1\"");
141 }
142
143 #[test]
144 fn test_format_datetime() {
145 let proto = ProtoTxt;
146 let dt = chrono::NaiveDateTime::parse_from_str("2024-01-15 10:30:45", "%Y-%m-%d %H:%M:%S")
147 .unwrap();
148 let result = proto.format_datetime(&dt);
149 assert!(result.starts_with('"'));
150 assert!(result.ends_with('"'));
151 assert!(result.contains("2024"));
152 }
153
154 #[test]
155 fn test_format_field() {
156 let proto = ProtoTxt;
157 let field = DataField::from_chars("name", "Alice");
158 let result = proto.format_field(&field);
159 assert_eq!(result, "name: \"Alice\"");
160 }
161
162 #[test]
163 fn test_format_field_digit() {
164 let proto = ProtoTxt;
165 let field = DataField::from_digit("age", 30);
166 let result = proto.format_field(&field);
167 assert_eq!(result, "age: 30");
168 }
169
170 #[test]
171 fn test_format_record() {
172 let proto = ProtoTxt;
173 let record = DataRecord {
174 items: vec![
175 DataField::from_chars("name", "Alice"),
176 DataField::from_digit("age", 30),
177 ],
178 };
179 let result = proto.format_record(&record);
180 assert!(result.starts_with("{ "));
181 assert!(result.ends_with(" }"));
182 assert!(result.contains("name: \"Alice\""));
183 assert!(result.contains("age: 30"));
184 }
185
186 #[test]
187 fn test_format_array() {
188 let proto = ProtoTxt;
189 let arr = vec![DataField::from_digit("x", 1), DataField::from_digit("y", 2)];
190 let result = proto.format_array(&arr);
191 assert!(result.starts_with('['));
192 assert!(result.ends_with(']'));
193 }
194}