cuenv_codegen/
formatter.rs1use crate::codegen::FormatConfig;
6use crate::{CodegenError, Result};
7
8#[derive(Debug)]
10pub struct Formatter;
11
12impl Formatter {
13 #[must_use]
15 pub const fn new() -> Self {
16 Self
17 }
18
19 pub fn format(&self, content: &str, language: &str, config: &FormatConfig) -> Result<String> {
25 match language {
26 "json" => self.format_json(content, config),
27 "typescript" | "javascript" => {
28 Ok(content.to_string())
31 }
32 "rust" => {
33 Ok(content.to_string())
36 }
37 _ => Ok(content.to_string()),
38 }
39 }
40
41 #[allow(clippy::unused_self)] fn format_json(&self, content: &str, config: &FormatConfig) -> Result<String> {
44 let value: serde_json::Value = serde_json::from_str(content)?;
45
46 let indent_size = config.indent_size.unwrap_or(2);
47 let indent_char = if config.indent == "tab" { '\t' } else { ' ' };
48
49 let formatted = if indent_char == '\t' {
50 serde_json::to_string_pretty(&value)?
51 } else {
52 let mut buf = Vec::new();
53 let indent = vec![b' '; indent_size];
54 let formatter = serde_json::ser::PrettyFormatter::with_indent(indent.as_slice());
55 let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
56 serde::Serialize::serialize(&value, &mut ser)
57 .map_err(|e| CodegenError::Formatting(e.to_string()))?;
58 String::from_utf8(buf).map_err(|e| CodegenError::Formatting(e.to_string()))?
59 };
60
61 Ok(formatted)
62 }
63}
64
65impl Default for Formatter {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn test_formatter_new() {
77 let fmt = Formatter::new();
78 let _debug = format!("{fmt:?}");
80 }
81
82 #[test]
83 fn test_formatter_default() {
84 let fmt = Formatter;
85 let config = FormatConfig::default();
87 let result = fmt.format("test", "unknown", &config);
88 assert!(result.is_ok());
89 }
90
91 #[test]
92 fn test_format_json() {
93 let fmt = Formatter::new();
94 let input = r#"{"name":"test","value":123}"#;
95 let config = FormatConfig {
96 indent: "space".to_string(),
97 indent_size: Some(2),
98 ..Default::default()
99 };
100
101 let result = fmt.format(input, "json", &config);
102 assert!(result.is_ok());
103
104 let output = result.unwrap();
105 assert!(output.contains(" ")); assert!(output.contains("\"name\": \"test\""));
107 }
108
109 #[test]
110 fn test_format_json_with_tabs() {
111 let fmt = Formatter::new();
112 let input = r#"{"key":"value"}"#;
113 let config = FormatConfig {
114 indent: "tab".to_string(),
115 indent_size: None,
116 ..Default::default()
117 };
118
119 let result = fmt.format(input, "json", &config);
120 assert!(result.is_ok());
121 let output = result.unwrap();
123 assert!(output.contains("\"key\":"));
124 }
125
126 #[test]
127 fn test_format_json_with_custom_indent_size() {
128 let fmt = Formatter::new();
129 let input = r#"{"a":"b"}"#;
130 let config = FormatConfig {
131 indent: "space".to_string(),
132 indent_size: Some(4),
133 ..Default::default()
134 };
135
136 let result = fmt.format(input, "json", &config);
137 assert!(result.is_ok());
138 let output = result.unwrap();
139 assert!(output.contains(" \"a\""));
141 }
142
143 #[test]
144 fn test_format_json_invalid() {
145 let fmt = Formatter::new();
146 let input = "{ not valid json }";
147 let config = FormatConfig::default();
148
149 let result = fmt.format(input, "json", &config);
150 assert!(result.is_err());
151 }
152
153 #[test]
154 fn test_format_typescript_passthrough() {
155 let fmt = Formatter::new();
156 let input = "const x = 1;";
157 let config = FormatConfig::default();
158
159 let result = fmt.format(input, "typescript", &config);
160 assert!(result.is_ok());
161 assert_eq!(result.unwrap(), input);
162 }
163
164 #[test]
165 fn test_format_javascript_passthrough() {
166 let fmt = Formatter::new();
167 let input = "function foo() { return 42; }";
168 let config = FormatConfig::default();
169
170 let result = fmt.format(input, "javascript", &config);
171 assert!(result.is_ok());
172 assert_eq!(result.unwrap(), input);
173 }
174
175 #[test]
176 fn test_format_rust_passthrough() {
177 let fmt = Formatter::new();
178 let input = "fn main() { println!(\"hello\"); }";
179 let config = FormatConfig::default();
180
181 let result = fmt.format(input, "rust", &config);
182 assert!(result.is_ok());
183 assert_eq!(result.unwrap(), input);
184 }
185
186 #[test]
187 fn test_format_unknown_language() {
188 let formatter = Formatter::new();
189 let input = "some content";
190 let config = FormatConfig::default();
191
192 let result = formatter.format(input, "unknown", &config);
193 assert!(result.is_ok());
194 assert_eq!(result.unwrap(), input);
195 }
196
197 #[test]
198 fn test_format_json_nested_structure() {
199 let fmt = Formatter::new();
200 let input = r#"{"outer":{"inner":{"deep":"value"}}}"#;
201 let config = FormatConfig {
202 indent: "space".to_string(),
203 indent_size: Some(2),
204 ..Default::default()
205 };
206
207 let result = fmt.format(input, "json", &config);
208 assert!(result.is_ok());
209 let output = result.unwrap();
210 assert!(output.contains("outer"));
211 assert!(output.contains("inner"));
212 assert!(output.contains("deep"));
213 }
214
215 #[test]
216 fn test_format_json_array() {
217 let fmt = Formatter::new();
218 let input = r"[1,2,3]";
219 let config = FormatConfig::default();
220
221 let result = fmt.format(input, "json", &config);
222 assert!(result.is_ok());
223 let output = result.unwrap();
224 assert!(output.contains('1'));
225 assert!(output.contains('2'));
226 assert!(output.contains('3'));
227 }
228}