Skip to main content

pick/formats/
yaml.rs

1use crate::error::PickError;
2use serde_json::Value;
3
4pub fn parse(input: &str) -> Result<Value, PickError> {
5    let yaml_value: serde_yaml::Value = serde_yaml::from_str(input)
6        .map_err(|e| PickError::ParseError("YAML".into(), e.to_string()))?;
7    yaml_to_json(yaml_value)
8}
9
10fn yaml_to_json(v: serde_yaml::Value) -> Result<Value, PickError> {
11    match v {
12        serde_yaml::Value::Null => Ok(Value::Null),
13        serde_yaml::Value::Bool(b) => Ok(Value::Bool(b)),
14        serde_yaml::Value::Number(n) => {
15            if let Some(i) = n.as_i64() {
16                Ok(Value::Number(i.into()))
17            } else if let Some(u) = n.as_u64() {
18                Ok(Value::Number(u.into()))
19            } else if let Some(f) = n.as_f64() {
20                Ok(serde_json::Number::from_f64(f)
21                    .map(Value::Number)
22                    .unwrap_or(Value::Null))
23            } else {
24                Ok(Value::Null)
25            }
26        }
27        serde_yaml::Value::String(s) => Ok(Value::String(s)),
28        serde_yaml::Value::Sequence(seq) => {
29            let items: Result<Vec<Value>, _> = seq.into_iter().map(yaml_to_json).collect();
30            Ok(Value::Array(items?))
31        }
32        serde_yaml::Value::Mapping(map) => {
33            let mut obj = serde_json::Map::new();
34            for (k, v) in map {
35                let key = match k {
36                    serde_yaml::Value::String(s) => s,
37                    serde_yaml::Value::Number(n) => n.to_string(),
38                    serde_yaml::Value::Bool(b) => b.to_string(),
39                    _ => format!("{k:?}"),
40                };
41                obj.insert(key, yaml_to_json(v)?);
42            }
43            Ok(Value::Object(obj))
44        }
45        serde_yaml::Value::Tagged(tagged) => yaml_to_json(tagged.value),
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use serde_json::json;
53
54    #[test]
55    fn parse_simple_kv() {
56        let v = parse("name: Alice\nage: 30").unwrap();
57        assert_eq!(v["name"], json!("Alice"));
58        assert_eq!(v["age"], json!(30));
59    }
60
61    #[test]
62    fn parse_nested() {
63        let input = "server:\n  host: localhost\n  port: 8080";
64        let v = parse(input).unwrap();
65        assert_eq!(v["server"]["host"], json!("localhost"));
66        assert_eq!(v["server"]["port"], json!(8080));
67    }
68
69    #[test]
70    fn parse_list() {
71        let input = "items:\n  - one\n  - two\n  - three";
72        let v = parse(input).unwrap();
73        assert_eq!(v["items"], json!(["one", "two", "three"]));
74    }
75
76    #[test]
77    fn parse_document_separator() {
78        let input = "---\nname: Alice";
79        let v = parse(input).unwrap();
80        assert_eq!(v["name"], json!("Alice"));
81    }
82
83    #[test]
84    fn parse_boolean_values() {
85        let input = "active: true\ndeleted: false";
86        let v = parse(input).unwrap();
87        assert_eq!(v["active"], json!(true));
88        assert_eq!(v["deleted"], json!(false));
89    }
90
91    #[test]
92    fn parse_null_value() {
93        let input = "value: null\nother: ~";
94        let v = parse(input).unwrap();
95        assert_eq!(v["value"], Value::Null);
96        assert_eq!(v["other"], Value::Null);
97    }
98
99    #[test]
100    fn parse_quoted_string() {
101        let input = "msg: \"hello world\"";
102        let v = parse(input).unwrap();
103        assert_eq!(v["msg"], json!("hello world"));
104    }
105
106    #[test]
107    fn parse_multiline_string() {
108        let input = "desc: |\n  line one\n  line two";
109        let v = parse(input).unwrap();
110        assert!(v["desc"].as_str().unwrap().contains("line one"));
111    }
112
113    #[test]
114    fn parse_complex_structure() {
115        let input = r#"
116users:
117  - name: Alice
118    age: 30
119  - name: Bob
120    age: 25
121"#;
122        let v = parse(input).unwrap();
123        assert_eq!(v["users"][0]["name"], json!("Alice"));
124        assert_eq!(v["users"][1]["age"], json!(25));
125    }
126
127    #[test]
128    fn parse_numeric_key() {
129        let input = "200: OK\n404: Not Found";
130        let v = parse(input).unwrap();
131        assert_eq!(v["200"], json!("OK"));
132    }
133
134    #[test]
135    fn parse_invalid() {
136        assert!(parse(":\n  :\n    :").is_err());
137    }
138}