1pub mod ast;
2pub mod parser;
3
4pub use ast::{Number, Value};
5pub use parser::{parse, apply_builtin_types, ParseError, ParseResult};
6
7/// Parse and stringify in one go (for convenience)
8pub fn stringify(input: &str, apply_types: bool) -> ParseResult<String> {
9 let mut values = parse(input)?;
10 if apply_types {
11 values = values.into_iter().map(apply_builtin_types).collect();
12 }
13
14 if values.len() == 1 {
15 Ok(values[0].to_uson_string())
16 } else {
17 let strings: Vec<String> = values.iter().map(|v| v.to_uson_string()).collect();
18 Ok(strings.join(" "))
19 }
20}
21
22/// Parse and convert to JSON string
23pub fn to_json(input: &str, apply_types: bool, pretty: bool) -> ParseResult<String> {
24 let mut values = parse(input)?;
25 if apply_types {
26 values = values.into_iter().map(apply_builtin_types).collect();
27 }
28
29 let json_value = serde_json::to_value(&values)
30 .map_err(|e| ParseError::NumberError(e.to_string()))?;
31
32 if pretty {
33 serde_json::to_string_pretty(&json_value)
34 .map_err(|e| ParseError::NumberError(e.to_string()))
35 } else {
36 serde_json::to_string(&json_value)
37 .map_err(|e| ParseError::NumberError(e.to_string()))
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use pretty_assertions::assert_eq;
45
46 #[test]
47 fn test_null() {
48 let result = parse("null").unwrap();
49 assert_eq!(result, vec![Value::Null]);
50 }
51
52 #[test]
53 fn test_bool() {
54 assert_eq!(parse("true").unwrap(), vec![Value::Bool(true)]);
55 assert_eq!(parse("false").unwrap(), vec![Value::Bool(false)]);
56 }
57
58 #[test]
59 fn test_numbers() {
60 assert_eq!(
61 parse("42").unwrap(),
62 vec![Value::Number(Number::Integer(42))]
63 );
64 assert_eq!(
65 parse("3.14").unwrap(),
66 vec![Value::Number(Number::Float(3.14))]
67 );
68 assert_eq!(
69 parse("-10").unwrap(),
70 vec![Value::Number(Number::Integer(-10))]
71 );
72 }
73
74 #[test]
75 fn test_strings() {
76 assert_eq!(
77 parse("\"hello\"").unwrap(),
78 vec![Value::String("hello".to_string())]
79 );
80 assert_eq!(
81 parse("'world'").unwrap(),
82 vec![Value::String("world".to_string())]
83 );
84 assert_eq!(
85 parse("unquoted").unwrap(),
86 vec![Value::String("unquoted".to_string())]
87 );
88 }
89
90 #[test]
91 fn test_array() {
92 let result = parse("[1, 2, 3]").unwrap();
93 assert_eq!(
94 result,
95 vec![Value::Array(vec![
96 Value::Number(Number::Integer(1)),
97 Value::Number(Number::Integer(2)),
98 Value::Number(Number::Integer(3))
99 ])]
100 );
101 }
102
103 #[test]
104 fn test_array_no_commas() {
105 let result = parse("[1 2 3]").unwrap();
106 assert_eq!(
107 result,
108 vec![Value::Array(vec![
109 Value::Number(Number::Integer(1)),
110 Value::Number(Number::Integer(2)),
111 Value::Number(Number::Integer(3))
112 ])]
113 );
114 }
115
116 #[test]
117 fn test_object() {
118 let result = parse(r#"{"key": "value"}"#).unwrap();
119 let mut expected = std::collections::HashMap::new();
120 expected.insert("key".to_string(), Value::String("value".to_string()));
121 assert_eq!(result, vec![Value::Object(expected)]);
122 }
123
124 #[test]
125 fn test_comment() {
126 let result = parse("# comment\n42").unwrap();
127 assert_eq!(result, vec![Value::Number(Number::Integer(42))]);
128 }
129
130 #[test]
131 fn test_typed() {
132 let result = parse("date!\"2024-01-01\"").unwrap();
133 assert_eq!(
134 result,
135 vec![Value::Typed {
136 type_name: "date".to_string(),
137 value: Box::new(Value::String("2024-01-01".to_string()))
138 }]
139 );
140 }
141
142 #[test]
143 fn test_assign() {
144 let result = parse(r#"name: "John""#).unwrap();
145 let mut expected = std::collections::HashMap::new();
146 expected.insert("name".to_string(), Value::String("John".to_string()));
147 assert_eq!(result, vec![Value::Object(expected)]);
148 }
149
150 #[test]
151 fn test_multiple_expressions() {
152 let result = parse("1 2 3").unwrap();
153 assert_eq!(
154 result,
155 vec![
156 Value::Number(Number::Integer(1)),
157 Value::Number(Number::Integer(2)),
158 Value::Number(Number::Integer(3))
159 ]
160 );
161 }
162
163 #[test]
164 fn test_stringify_simple() {
165 let value = Value::String("hello".to_string());
166 assert_eq!(value.to_uson_string(), "hello");
167 }
168
169 #[test]
170 fn test_stringify_number() {
171 let value = Value::Number(Number::Integer(42));
172 assert_eq!(value.to_uson_string(), "42");
173 }
174
175 #[test]
176 fn test_stringify_array() {
177 let value = Value::Array(vec![
178 Value::Number(Number::Integer(1)),
179 Value::Number(Number::Integer(2)),
180 Value::Number(Number::Integer(3)),
181 ]);
182 assert_eq!(value.to_uson_string(), "[1 2 3]");
183 }
184
185 #[test]
186 fn test_stringify_object() {
187 let mut obj = std::collections::HashMap::new();
188 obj.insert("name".to_string(), Value::String("John".to_string()));
189 obj.insert("age".to_string(), Value::Number(Number::Integer(30)));
190 let value = Value::Object(obj);
191 let result = value.to_uson_string();
192 // Note: HashMap doesn't guarantee order, but our stringify sorts keys
193 assert!(result.contains("age:30"));
194 assert!(result.contains("name:John"));
195 }
196
197 #[test]
198 fn test_stringify_needs_quotes() {
199 let value = Value::String("hello world".to_string());
200 assert_eq!(value.to_uson_string(), "\"hello world\"");
201 }
202
203 #[test]
204 fn test_roundtrip() {
205 let input = "name:John age:30 active:true";
206 let parsed = parse(input).unwrap();
207 let stringified: Vec<String> = parsed.iter().map(|v| v.to_uson_string()).collect();
208 let result = stringified.join(" ");
209
210 // Parse again
211 let reparsed = parse(&result).unwrap();
212 assert_eq!(parsed, reparsed);
213 }
214}