c12_parser/
yaml_format.rs1use serde::{Serialize, de::DeserializeOwned};
2
3use crate::format::{FormatOptions, Formatted};
4
5pub fn parse_yaml<T>(
7 text: &str,
8 options: Option<FormatOptions>,
9) -> Result<Formatted<T>, serde_yaml::Error>
10where
11 T: DeserializeOwned,
12{
13 let mut opts = options.unwrap_or_default();
14 opts.preserve_indentation = false;
16 let value = serde_yaml::from_str(text)?;
17 Ok(Formatted::new(text, value, &opts))
18}
19
20pub fn stringify_yaml<T>(
22 formatted: &Formatted<T>,
23 options: Option<FormatOptions>,
24) -> Result<String, serde_yaml::Error>
25where
26 T: Serialize,
27{
28 let _opts = options.unwrap_or_default();
29
30 let yaml_str = serde_yaml::to_string(&formatted.value)?;
33
34 Ok(format!(
35 "{}{}{}",
36 formatted.format.whitespace_start, yaml_str, formatted.format.whitespace_end
37 ))
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43 use serde_json::Value as JsonValue;
44
45 const YAML_FIXTURE: &str = r#"
46types:
47 boolean: true
48 integer: 1
49 float: 3.14
50 string: hello
51 array:
52 - 1
53 - 2
54 - 3
55 object:
56 key: value
57 'null': null
58 date: 1979-05-27T15:32:00.000Z
59"#;
60
61 fn strip_line_comments(s: &str, prefix: &str) -> String {
62 s.lines()
63 .map(|line| {
64 if let Some(pos) = line.find(prefix) {
65 &line[..pos]
66 } else {
67 line
68 }
69 })
70 .collect::<Vec<_>>()
71 .join("\n")
72 }
73
74 #[test]
75 fn yaml_parse_ok() {
76 let formatted = parse_yaml::<serde_yaml::Value>(YAML_FIXTURE, None).unwrap();
77 let root = formatted.value;
78
79 let types = root
80 .get("types")
81 .expect("types key should exist")
82 .as_mapping()
83 .expect("types should be a mapping");
84
85 assert_eq!(
86 types.get(&serde_yaml::Value::String("boolean".into())),
87 Some(&serde_yaml::Value::Bool(true))
88 );
89 assert_eq!(
90 types.get(&serde_yaml::Value::String("integer".into())),
91 Some(&serde_yaml::Value::Number(1.into()))
92 );
93 assert_eq!(
94 types.get(&serde_yaml::Value::String("float".into())),
95 Some(&serde_yaml::Value::Number(serde_yaml::Number::from(3.14)))
96 );
97 assert_eq!(
98 types.get(&serde_yaml::Value::String("string".into())),
99 Some(&serde_yaml::Value::String("hello".into()))
100 );
101 assert_eq!(
102 types.get(&serde_yaml::Value::String("array".into())),
103 Some(&serde_yaml::Value::Sequence(vec![
104 serde_yaml::Value::Number(1.into()),
105 serde_yaml::Value::Number(2.into()),
106 serde_yaml::Value::Number(3.into()),
107 ]))
108 );
109 assert_eq!(
111 types.get(&serde_yaml::Value::String("null".into())),
112 Some(&serde_yaml::Value::Null)
113 );
114 assert_eq!(
115 types.get(&serde_yaml::Value::String("date".into())),
116 Some(&serde_yaml::Value::String(
117 "1979-05-27T15:32:00.000Z".into()
118 ))
119 );
120 }
121
122 #[test]
123 fn yaml_stringify_exact_without_comments_normalized_indent() {
124 let formatted = parse_yaml::<JsonValue>(YAML_FIXTURE, None).unwrap();
125 let out = stringify_yaml(&formatted, None).unwrap();
126
127 let without_comments = strip_line_comments(YAML_FIXTURE, "#");
128 let expected_val: serde_yaml::Value = serde_yaml::from_str(&without_comments).unwrap();
129
130 let out_val: serde_yaml::Value = serde_yaml::from_str(&out).unwrap();
131 assert_eq!(out_val, expected_val);
132 }
133
134 #[test]
135 fn yaml_preserves_outer_whitespace() {
136 let text = " \ntypes:\n key: value\n\n";
137 let formatted = parse_yaml::<JsonValue>(text, None).unwrap();
138 let out = stringify_yaml(&formatted, None).unwrap();
139
140 assert!(out.starts_with(" \n"));
141 assert!(out.ends_with("\n\n"));
142 }
143}