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}