1use crate::{RsonValue, RsonError, RsonResult};
7use core::fmt::Write;
8
9#[cfg(not(feature = "std"))]
10use alloc::{string::String, vec::Vec};
11
12#[derive(Debug, Clone)]
14pub struct FormatOptions {
15 pub indent_size: usize,
17 pub compact: bool,
19 pub trailing_commas: bool,
21 pub max_line_length: usize,
23 pub sort_keys: bool,
25}
26
27impl Default for FormatOptions {
28 fn default() -> Self {
29 Self {
30 indent_size: 2,
31 compact: false,
32 trailing_commas: true,
33 max_line_length: 80,
34 sort_keys: false,
35 }
36 }
37}
38
39impl FormatOptions {
40 pub fn compact() -> Self {
42 Self {
43 compact: true,
44 trailing_commas: false,
45 ..Default::default()
46 }
47 }
48
49 pub fn pretty() -> Self {
51 Self {
52 compact: false,
53 trailing_commas: true,
54 indent_size: 2,
55 ..Default::default()
56 }
57 }
58}
59
60pub struct Formatter<'a> {
62 options: &'a FormatOptions,
63 output: String,
64 indent_level: usize,
65}
66
67impl<'a> Formatter<'a> {
68 pub fn new(options: &'a FormatOptions) -> Self {
70 Self {
71 options,
72 output: String::new(),
73 indent_level: 0,
74 }
75 }
76
77 pub fn format(mut self, value: &RsonValue) -> RsonResult<String> {
79 self.format_value(value)?;
80 Ok(self.output)
81 }
82
83 fn format_value(&mut self, value: &RsonValue) -> RsonResult<()> {
85 match value {
86 RsonValue::Null => self.write_str("null"),
87 RsonValue::Bool(b) => self.write_str(&b.to_string()),
88 RsonValue::Int(i) => self.write_str(&i.to_string()),
89 RsonValue::Float(f) => self.write_str(&f.to_string()),
90 RsonValue::String(s) => self.format_string(s),
91 RsonValue::Char(c) => self.format_char(*c),
92 RsonValue::Array(arr) => self.format_array(arr),
93 RsonValue::Map(map) => self.format_map(map),
94 RsonValue::Struct { name, fields } => self.format_struct(name, fields),
95 RsonValue::Tuple(values) => self.format_tuple(values),
96 RsonValue::Enum { name, variant, value } => self.format_enum(name, variant, value.as_ref().map(|v| &**v)),
97 RsonValue::Option(opt) => self.format_option(opt.as_ref().map(|v| &**v)),
98 }
99 }
100
101 fn write_str(&mut self, s: &str) -> RsonResult<()> {
103 self.output.push_str(s);
104 Ok(())
105 }
106
107 fn write_char(&mut self, c: char) -> RsonResult<()> {
109 self.output.push(c);
110 Ok(())
111 }
112
113 fn write_indent(&mut self) -> RsonResult<()> {
115 if !self.options.compact {
116 for _ in 0..(self.indent_level * self.options.indent_size) {
117 self.write_char(' ')?;
118 }
119 }
120 Ok(())
121 }
122
123 fn write_newline(&mut self) -> RsonResult<()> {
125 if !self.options.compact {
126 self.write_char('\n')?;
127 }
128 Ok(())
129 }
130
131 fn write_space(&mut self) -> RsonResult<()> {
133 if !self.options.compact {
134 self.write_char(' ')?;
135 }
136 Ok(())
137 }
138
139 fn format_string(&mut self, s: &str) -> RsonResult<()> {
141 self.write_char('"')?;
142 for c in s.chars() {
143 match c {
144 '"' => self.write_str("\\\"")?,
145 '\\' => self.write_str("\\\\")?,
146 '\n' => self.write_str("\\n")?,
147 '\r' => self.write_str("\\r")?,
148 '\t' => self.write_str("\\t")?,
149 '\0' => self.write_str("\\0")?,
150 c if c.is_control() => {
151 write!(self.output, "\\u{:04x}", c as u32)
152 .map_err(|_| RsonError::custom("Failed to write Unicode escape"))?;
153 }
154 c => self.write_char(c)?,
155 }
156 }
157 self.write_char('"')?;
158 Ok(())
159 }
160
161 fn format_char(&mut self, c: char) -> RsonResult<()> {
163 self.write_char('\'')?;
164 match c {
165 '\'' => self.write_str("\\'")?,
166 '\\' => self.write_str("\\\\")?,
167 '\n' => self.write_str("\\n")?,
168 '\r' => self.write_str("\\r")?,
169 '\t' => self.write_str("\\t")?,
170 '\0' => self.write_str("\\0")?,
171 c if c.is_control() => {
172 write!(self.output, "\\u{:04x}", c as u32)
173 .map_err(|_| RsonError::custom("Failed to write Unicode escape"))?;
174 }
175 c => self.write_char(c)?,
176 }
177 self.write_char('\'')?;
178 Ok(())
179 }
180
181 fn format_array(&mut self, arr: &[RsonValue]) -> RsonResult<()> {
183 self.write_char('[')?;
184
185 if arr.is_empty() {
186 self.write_char(']')?;
187 return Ok(());
188 }
189
190 let multiline = !self.options.compact && self.should_be_multiline_array(arr);
191
192 if multiline {
193 self.write_newline()?;
194 self.indent_level += 1;
195 }
196
197 for (i, item) in arr.iter().enumerate() {
198 if i > 0 {
199 self.write_char(',')?;
200 if multiline {
201 self.write_newline()?;
202 } else {
203 self.write_space()?;
204 }
205 }
206
207 if multiline {
208 self.write_indent()?;
209 }
210
211 self.format_value(item)?;
212 }
213
214 if self.options.trailing_commas && !arr.is_empty() {
215 self.write_char(',')?;
216 }
217
218 if multiline {
219 self.write_newline()?;
220 self.indent_level -= 1;
221 self.write_indent()?;
222 }
223
224 self.write_char(']')?;
225 Ok(())
226 }
227
228 fn format_map(&mut self, map: &indexmap::IndexMap<String, RsonValue>) -> RsonResult<()> {
230 self.write_char('{')?;
231
232 if map.is_empty() {
233 self.write_char('}')?;
234 return Ok(());
235 }
236
237 let multiline = !self.options.compact && self.should_be_multiline_map(map);
238
239 if multiline {
240 self.write_newline()?;
241 self.indent_level += 1;
242 }
243
244 let mut entries: Vec<_> = map.iter().collect();
245 if self.options.sort_keys {
246 entries.sort_by_key(|(key, _)| *key);
247 }
248
249 for (i, (key, value)) in entries.iter().enumerate() {
250 if i > 0 {
251 self.write_char(',')?;
252 if multiline {
253 self.write_newline()?;
254 } else {
255 self.write_space()?;
256 }
257 }
258
259 if multiline {
260 self.write_indent()?;
261 }
262
263 self.format_map_key(key)?;
264 self.write_char(':')?;
265 self.write_space()?;
266 self.format_value(value)?;
267 }
268
269 if self.options.trailing_commas && !map.is_empty() {
270 self.write_char(',')?;
271 }
272
273 if multiline {
274 self.write_newline()?;
275 self.indent_level -= 1;
276 self.write_indent()?;
277 }
278
279 self.write_char('}')?;
280 Ok(())
281 }
282
283 fn format_map_key(&mut self, key: &str) -> RsonResult<()> {
285 if is_valid_identifier(key) {
286 self.write_str(key)?;
287 } else {
288 self.format_string(key)?;
289 }
290 Ok(())
291 }
292
293 fn format_struct(&mut self, name: &str, fields: &indexmap::IndexMap<String, RsonValue>) -> RsonResult<()> {
295 self.write_str(name)?;
296 self.write_char('(')?;
297
298 if fields.is_empty() {
299 self.write_char(')')?;
300 return Ok(());
301 }
302
303 let multiline = !self.options.compact && self.should_be_multiline_struct(fields);
304
305 if multiline {
306 self.write_newline()?;
307 self.indent_level += 1;
308 }
309
310 for (i, (field_name, value)) in fields.iter().enumerate() {
311 if i > 0 {
312 self.write_char(',')?;
313 if multiline {
314 self.write_newline()?;
315 } else {
316 self.write_space()?;
317 }
318 }
319
320 if multiline {
321 self.write_indent()?;
322 }
323
324 self.write_str(field_name)?;
325 self.write_char(':')?;
326 self.write_space()?;
327 self.format_value(value)?;
328 }
329
330 if self.options.trailing_commas && !fields.is_empty() {
331 self.write_char(',')?;
332 }
333
334 if multiline {
335 self.write_newline()?;
336 self.indent_level -= 1;
337 self.write_indent()?;
338 }
339
340 self.write_char(')')?;
341 Ok(())
342 }
343
344 fn format_tuple(&mut self, values: &[RsonValue]) -> RsonResult<()> {
346 self.write_char('(')?;
347
348 for (i, value) in values.iter().enumerate() {
349 if i > 0 {
350 self.write_char(',')?;
351 self.write_space()?;
352 }
353 self.format_value(value)?;
354 }
355
356 if values.len() == 1 || (self.options.trailing_commas && !values.is_empty()) {
358 self.write_char(',')?;
359 }
360
361 self.write_char(')')?;
362 Ok(())
363 }
364
365 fn format_enum(&mut self, name: &str, variant: &str, value: Option<&RsonValue>) -> RsonResult<()> {
367 self.write_str(name)?;
368 self.write_str("::")?;
369 self.write_str(variant)?;
370
371 if let Some(val) = value {
372 self.write_char('(')?;
373 self.format_value(val)?;
374 self.write_char(')')?;
375 }
376
377 Ok(())
378 }
379
380 fn format_option(&mut self, value: Option<&RsonValue>) -> RsonResult<()> {
382 match value {
383 Some(val) => {
384 self.write_str("Some(")?;
385 self.format_value(val)?;
386 self.write_char(')')?;
387 }
388 None => self.write_str("None")?,
389 }
390 Ok(())
391 }
392
393 fn should_be_multiline_array(&self, arr: &[RsonValue]) -> bool {
395 if self.options.compact {
396 return false;
397 }
398
399 arr.len() > 3 || arr.iter().any(|v| matches!(v,
401 RsonValue::Array(_) |
402 RsonValue::Map(_) |
403 RsonValue::Struct { .. }
404 ))
405 }
406
407 fn should_be_multiline_map(&self, map: &indexmap::IndexMap<String, RsonValue>) -> bool {
409 if self.options.compact {
410 return false;
411 }
412
413 map.len() > 1 || map.values().any(|v| matches!(v,
415 RsonValue::Array(_) |
416 RsonValue::Map(_) |
417 RsonValue::Struct { .. }
418 ))
419 }
420
421 fn should_be_multiline_struct(&self, fields: &indexmap::IndexMap<String, RsonValue>) -> bool {
423 if self.options.compact {
424 return false;
425 }
426
427 fields.len() > 2 || fields.values().any(|v| matches!(v,
429 RsonValue::Array(_) |
430 RsonValue::Map(_) |
431 RsonValue::Struct { .. }
432 ))
433 }
434}
435
436fn is_valid_identifier(s: &str) -> bool {
438 if s.is_empty() {
439 return false;
440 }
441
442 match s {
444 "true" | "false" | "null" | "Some" | "None" => return false,
445 _ => {}
446 }
447
448 let mut chars = s.chars();
449 let first = chars.next().unwrap();
450
451 if !first.is_alphabetic() && first != '_' {
453 return false;
454 }
455
456 chars.all(|c| c.is_alphanumeric() || c == '_')
458}
459
460pub fn format_rson(value: &RsonValue, options: &FormatOptions) -> RsonResult<String> {
462 let formatter = Formatter::new(options);
463 formatter.format(value)
464}
465
466pub fn format_pretty(value: &RsonValue) -> RsonResult<String> {
468 format_rson(value, &FormatOptions::pretty())
469}
470
471pub fn format_compact(value: &RsonValue) -> RsonResult<String> {
473 format_rson(value, &FormatOptions::compact())
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479 use crate::RsonValue;
480 use indexmap::IndexMap;
481
482 #[test]
483 fn test_format_primitives() {
484 assert_eq!(format_compact(&RsonValue::Null).unwrap(), "null");
485 assert_eq!(format_compact(&RsonValue::Bool(true)).unwrap(), "true");
486 assert_eq!(format_compact(&RsonValue::Bool(false)).unwrap(), "false");
487 assert_eq!(format_compact(&RsonValue::Int(42)).unwrap(), "42");
488 assert_eq!(format_compact(&RsonValue::Float(3.14)).unwrap(), "3.14");
489 assert_eq!(format_compact(&RsonValue::String("hello".to_string())).unwrap(), r#""hello""#);
490 assert_eq!(format_compact(&RsonValue::Char('a')).unwrap(), "'a'");
491 }
492
493 #[test]
494 fn test_format_array() {
495 let arr = RsonValue::Array(vec![
496 RsonValue::Int(1),
497 RsonValue::Int(2),
498 RsonValue::Int(3),
499 ]);
500
501 assert_eq!(format_compact(&arr).unwrap(), "[1,2,3]");
502
503 let pretty = format_pretty(&arr).unwrap();
504 assert!(pretty.contains("["));
505 assert!(pretty.contains("1,"));
506 assert!(pretty.contains("2,"));
507 assert!(pretty.contains("3,"));
508 assert!(pretty.contains("]"));
509 }
510
511 #[test]
512 fn test_format_map() {
513 let mut map = IndexMap::new();
514 map.insert("name".to_string(), RsonValue::String("Alice".to_string()));
515 map.insert("age".to_string(), RsonValue::Int(30));
516
517 let value = RsonValue::Map(map);
518 let formatted = format_compact(&value).unwrap();
519
520 assert!(formatted.contains("name:\"Alice\""));
521 assert!(formatted.contains("age:30"));
522 }
523
524 #[test]
525 fn test_format_struct() {
526 let mut fields = IndexMap::new();
527 fields.insert("x".to_string(), RsonValue::Int(10));
528 fields.insert("y".to_string(), RsonValue::Int(20));
529
530 let value = RsonValue::Struct {
531 name: "Point".to_string(),
532 fields,
533 };
534
535 let formatted = format_compact(&value).unwrap();
536 assert_eq!(formatted, "Point(x:10,y:20)");
537 }
538
539 #[test]
540 fn test_format_enum() {
541 let enum_val = RsonValue::Enum {
542 name: "Color".to_string(),
543 variant: "Red".to_string(),
544 value: None,
545 };
546
547 assert_eq!(format_compact(&enum_val).unwrap(), "Color::Red");
548
549 let enum_with_value = RsonValue::Enum {
550 name: "Result".to_string(),
551 variant: "Ok".to_string(),
552 value: Some(Box::new(RsonValue::String("success".to_string()))),
553 };
554
555 assert_eq!(format_compact(&enum_with_value).unwrap(), r#"Result::Ok("success")"#);
556 }
557
558 #[test]
559 fn test_format_option() {
560 assert_eq!(format_compact(&RsonValue::Option(None)).unwrap(), "None");
561 assert_eq!(
562 format_compact(&RsonValue::Option(Some(Box::new(RsonValue::Int(42))))).unwrap(),
563 "Some(42)"
564 );
565 }
566
567 #[test]
568 fn test_string_escaping() {
569 let value = RsonValue::String("hello\nworld\"test".to_string());
570 let formatted = format_compact(&value).unwrap();
571 assert_eq!(formatted, r#""hello\nworld\"test""#);
572 }
573
574 #[test]
575 fn test_identifier_validation() {
576 assert!(is_valid_identifier("hello"));
577 assert!(is_valid_identifier("_private"));
578 assert!(is_valid_identifier("field1"));
579 assert!(!is_valid_identifier("123invalid"));
580 assert!(!is_valid_identifier("true"));
581 assert!(!is_valid_identifier(""));
582 assert!(!is_valid_identifier("hello-world"));
583 }
584}