1use crate::ast::AstPattern;
28use pattern_core::{Pattern, RangeValue, Subject, Symbol, Value};
29use std::collections::{HashMap, HashSet};
30
31pub fn gram_parse_to_json(input: &str) -> Result<String, String> {
52 if input.trim().is_empty() {
53 return Ok("[]".to_string());
54 }
55 let patterns = crate::parse_gram(input).map_err(|e| e.to_string())?;
56 let asts: Vec<AstPattern> = patterns.iter().map(AstPattern::from_pattern).collect();
57 serde_json::to_string(&asts).map_err(|e| e.to_string())
58}
59
60pub fn gram_stringify_from_json(input: &str) -> Result<String, String> {
80 let asts: Vec<AstPattern> = serde_json::from_str(input).map_err(|e| e.to_string())?;
81 let patterns: Vec<Pattern<Subject>> = asts
82 .iter()
83 .map(ast_to_pattern)
84 .collect::<Result<Vec<_>, _>>()?;
85 let gram_parts: Result<Vec<String>, String> = patterns
86 .iter()
87 .map(|p| crate::to_gram_pattern(p).map_err(|e| e.to_string()))
88 .collect();
89 Ok(gram_parts?.join(" "))
90}
91
92pub fn gram_validate_to_json(input: &str) -> String {
102 match crate::validate_gram(input) {
103 Ok(()) => "[]".to_string(),
104 Err(e) => {
105 let msg = e.to_string();
106 serde_json::to_string(&[msg]).unwrap_or_else(|_| "[]".to_string())
107 }
108 }
109}
110
111pub fn ast_to_pattern(ast: &AstPattern) -> Result<Pattern<Subject>, String> {
113 let subject = Subject {
114 identity: Symbol(ast.subject.identity.clone()),
115 labels: ast.subject.labels.iter().cloned().collect::<HashSet<_>>(),
116 properties: ast
117 .subject
118 .properties
119 .iter()
120 .map(|(k, v)| json_to_value(v).map(|val| (k.clone(), val)))
121 .collect::<Result<HashMap<_, _>, _>>()?,
122 };
123 let elements: Vec<Pattern<Subject>> = ast
124 .elements
125 .iter()
126 .map(ast_to_pattern)
127 .collect::<Result<Vec<_>, _>>()?;
128 if elements.is_empty() {
129 Ok(Pattern::point(subject))
130 } else {
131 Ok(Pattern::pattern(subject, elements))
132 }
133}
134
135fn json_to_value(v: &serde_json::Value) -> Result<Value, String> {
137 match v {
138 serde_json::Value::String(s) => Ok(Value::VString(s.clone())),
139 serde_json::Value::Bool(b) => Ok(Value::VBoolean(*b)),
140 serde_json::Value::Null => {
141 Err("JSON null is not representable as a gram value".to_string())
142 }
143 serde_json::Value::Number(n) => {
144 if let Some(i) = n.as_i64() {
145 Ok(Value::VInteger(i))
146 } else if let Some(f) = n.as_f64() {
147 Ok(Value::VDecimal(f))
148 } else {
149 Err(format!(
150 "JSON number is not representable as a gram decimal value: {}",
151 n
152 ))
153 }
154 }
155 serde_json::Value::Array(arr) => {
156 let items: Vec<Value> = arr
157 .iter()
158 .map(json_to_value)
159 .collect::<Result<Vec<_>, _>>()?;
160 Ok(Value::VArray(items))
161 }
162 serde_json::Value::Object(obj) => {
163 if let Some(type_tag) = obj.get("type").and_then(|t| t.as_str()) {
165 match type_tag {
166 "symbol" => {
167 let val = obj
168 .get("value")
169 .and_then(|v| v.as_str())
170 .ok_or_else(|| "symbol value must be a string".to_string())?
171 .to_string();
172 Ok(Value::VSymbol(val))
173 }
174 "range" => {
175 let lower = obj.get("lower").and_then(|v| v.as_f64());
176 let upper = obj.get("upper").and_then(|v| v.as_f64());
177 Ok(Value::VRange(RangeValue { lower, upper }))
178 }
179 "measurement" => {
180 let unit = obj
181 .get("unit")
182 .and_then(|v| v.as_str())
183 .ok_or_else(|| "measurement unit must be a string".to_string())?
184 .to_string();
185 let value = obj
186 .get("value")
187 .and_then(|v| v.as_f64())
188 .ok_or_else(|| "measurement value must be a number".to_string())?;
189 Ok(Value::VMeasurement { unit, value })
190 }
191 "tagged" => {
192 let tag = obj
193 .get("tag")
194 .and_then(|v| v.as_str())
195 .ok_or_else(|| "tagged value tag must be a string".to_string())?
196 .to_string();
197 let content = obj
198 .get("content")
199 .and_then(|v| v.as_str())
200 .ok_or_else(|| "tagged value content must be a string".to_string())?
201 .to_string();
202 Ok(Value::VTaggedString { tag, content })
203 }
204 _ => Err(format!("unknown tagged value type: {}", type_tag)),
205 }
206 } else {
207 let map: HashMap<String, Value> = obj
209 .iter()
210 .map(|(k, v)| json_to_value(v).map(|val| (k.clone(), val)))
211 .collect::<Result<HashMap<_, _>, _>>()?;
212 Ok(Value::VMap(map))
213 }
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_parse_empty_input() {
224 assert_eq!(gram_parse_to_json("").unwrap(), "[]");
225 assert_eq!(gram_parse_to_json(" ").unwrap(), "[]");
226 }
227
228 #[test]
229 fn test_parse_simple_node() {
230 let json = gram_parse_to_json("(alice:Person)").unwrap();
231 let parsed: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
232 assert_eq!(parsed.len(), 1);
233 assert_eq!(parsed[0]["subject"]["identity"], "alice");
234 assert_eq!(parsed[0]["subject"]["labels"][0], "Person");
235 }
236
237 #[test]
238 fn test_parse_node_with_properties() {
239 let json = gram_parse_to_json(r#"(a {name: "Alice", age: 30})"#).unwrap();
240 let parsed: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
241 assert_eq!(parsed[0]["subject"]["properties"]["name"], "Alice");
242 assert_eq!(parsed[0]["subject"]["properties"]["age"], 30);
243 }
244
245 #[test]
246 fn test_parse_relationship() {
247 let json = gram_parse_to_json("(a)-->(b)").unwrap();
248 let parsed: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
249 assert_eq!(parsed.len(), 1);
250 assert_eq!(parsed[0]["elements"].as_array().unwrap().len(), 2);
251 }
252
253 #[test]
254 fn test_stringify_round_trip() {
255 let original = "(alice:Person)";
256 let json = gram_parse_to_json(original).unwrap();
257 let gram = gram_stringify_from_json(&json).unwrap();
258 let json2 = gram_parse_to_json(&gram).unwrap();
260 let p1: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
261 let p2: Vec<serde_json::Value> = serde_json::from_str(&json2).unwrap();
262 assert_eq!(p1[0]["subject"]["identity"], p2[0]["subject"]["identity"]);
263 assert_eq!(p1[0]["subject"]["labels"], p2[0]["subject"]["labels"]);
264 }
265
266 #[test]
267 fn test_validate_valid_input() {
268 let result = gram_validate_to_json("(alice:Person)");
269 let errors: Vec<String> = serde_json::from_str(&result).unwrap();
270 assert!(errors.is_empty());
271 }
272
273 #[test]
274 fn test_validate_invalid_input() {
275 let result = gram_validate_to_json("(((invalid");
276 let errors: Vec<String> = serde_json::from_str(&result).unwrap();
277 assert!(!errors.is_empty());
278 }
279
280 #[test]
281 fn test_json_interchange_format_subject_key() {
282 let json = gram_parse_to_json("(x)").unwrap();
284 assert!(json.contains("\"subject\""));
285 assert!(!json.contains("\"value\"") || json.contains("\"value\":"));
286 }
287
288 #[test]
289 fn test_value_types_in_json() {
290 let json = gram_parse_to_json(r#"(a {s: "hello", i: 42, f: 3.14, b: true})"#).unwrap();
291 let parsed: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
292 let props = &parsed[0]["subject"]["properties"];
293 assert!(props["s"].is_string());
294 assert!(props["i"].is_number());
295 assert!(props["f"].is_number());
296 assert!(props["b"].is_boolean());
297 }
298
299 #[test]
300 fn test_json_to_value_tagged_types() {
301 let v = json_to_value(&serde_json::json!({"type": "symbol", "value": "foo"})).unwrap();
303 assert!(matches!(v, Value::VSymbol(_)));
304
305 let v =
307 json_to_value(&serde_json::json!({"type": "measurement", "unit": "kg", "value": 5.0}))
308 .unwrap();
309 assert!(matches!(v, Value::VMeasurement { .. }));
310
311 let v = json_to_value(
313 &serde_json::json!({"type": "tagged", "tag": "date", "content": "2024-01-01"}),
314 )
315 .unwrap();
316 assert!(matches!(v, Value::VTaggedString { .. }));
317
318 let v = json_to_value(&serde_json::json!({"type": "range", "lower": 1.0, "upper": 10.0}))
320 .unwrap();
321 assert!(matches!(v, Value::VRange(_)));
322 }
323
324 #[test]
325 fn test_json_to_value_rejects_null() {
326 let err = json_to_value(&serde_json::Value::Null).unwrap_err();
327 assert!(err.contains("not representable"));
328 }
329
330 #[test]
331 fn test_json_to_value_rejects_unknown_tagged_type() {
332 let err = json_to_value(&serde_json::json!({"type": "unknown", "value": 1})).unwrap_err();
333 assert!(err.contains("unknown tagged value type"));
334 }
335
336 #[test]
337 fn test_stringify_rejects_null_property_in_json() {
338 let err = gram_stringify_from_json(
339 r#"[{"subject":{"identity":"alice","labels":["Person"],"properties":{"nickname":null}},"elements":[]}]"#,
340 )
341 .unwrap_err();
342 assert!(err.contains("not representable"));
343 }
344
345 #[test]
346 fn test_stringify_rejects_malformed_tagged_value() {
347 let err = gram_stringify_from_json(
348 r#"[{"subject":{"identity":"alice","labels":["Person"],"properties":{"code":{"type":"symbol"}}},"elements":[]}]"#,
349 )
350 .unwrap_err();
351 assert!(err.contains("symbol value must be a string"));
352 }
353}