μson (uson) is a shorthand for JSON
at main 6.3 kB view raw
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}