use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AstPattern {
pub subject: AstSubject,
pub elements: Vec<AstPattern>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AstSubject {
pub identity: String,
pub labels: Vec<String>,
pub properties: HashMap<String, serde_json::Value>,
}
impl AstPattern {
pub fn empty() -> Self {
AstPattern {
subject: AstSubject {
identity: String::new(),
labels: Vec::new(),
properties: HashMap::new(),
},
elements: Vec::new(),
}
}
}
use pattern_core::{Pattern, Subject, Value};
impl AstPattern {
pub fn from_pattern(pattern: &Pattern<Subject>) -> Self {
let subject = pattern.value();
AstPattern {
subject: AstSubject {
identity: subject.identity.0.clone(),
labels: subject.labels.iter().cloned().collect(),
properties: subject
.properties
.iter()
.map(|(k, v)| (k.clone(), value_to_json(v)))
.collect(),
},
elements: pattern
.elements()
.iter()
.map(AstPattern::from_pattern)
.collect(),
}
}
}
fn value_to_json(value: &Value) -> serde_json::Value {
match value {
Value::VInteger(i) => serde_json::Value::Number((*i).into()),
Value::VDecimal(d) => {
serde_json::Number::from_f64(*d)
.map(serde_json::Value::Number)
.unwrap_or_else(|| serde_json::Value::Null)
}
Value::VBoolean(b) => serde_json::Value::Bool(*b),
Value::VString(s) => serde_json::Value::String(s.clone()),
Value::VArray(arr) => serde_json::Value::Array(arr.iter().map(value_to_json).collect()),
Value::VMap(map) => serde_json::Value::Object(
map.iter()
.map(|(k, v)| (k.clone(), value_to_json(v)))
.collect(),
),
Value::VSymbol(sym) => serde_json::json!({
"type": "symbol",
"value": sym.clone()
}),
Value::VRange(range) => serde_json::json!({
"type": "range",
"lower": range.lower,
"upper": range.upper
}),
Value::VMeasurement { unit, value } => serde_json::json!({
"type": "measurement",
"unit": unit,
"value": value
}),
Value::VTaggedString { tag, content } => serde_json::json!({
"type": "tagged",
"tag": tag,
"content": content
}),
}
}
#[cfg(test)]
mod tests {
use super::*;
use pattern_core::{Pattern, Subject, Symbol};
use std::collections::{HashMap, HashSet};
#[test]
fn test_empty_pattern() {
let pattern = AstPattern::empty();
assert_eq!(pattern.subject.identity, "");
assert_eq!(pattern.subject.labels.len(), 0);
assert_eq!(pattern.subject.properties.len(), 0);
assert_eq!(pattern.elements.len(), 0);
}
#[test]
fn test_json_serialization() {
let pattern = AstPattern {
subject: AstSubject {
identity: "alice".to_string(),
labels: vec!["Person".to_string()],
properties: {
let mut props = HashMap::new();
props.insert("name".to_string(), serde_json::json!("Alice"));
props
},
},
elements: vec![],
};
let json = serde_json::to_string(&pattern).unwrap();
assert!(json.contains("alice"));
assert!(json.contains("Person"));
let deserialized: AstPattern = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, pattern);
}
#[test]
fn test_nested_patterns() {
let child1 = AstPattern {
subject: AstSubject {
identity: "child1".to_string(),
labels: vec![],
properties: HashMap::new(),
},
elements: vec![],
};
let child2 = AstPattern {
subject: AstSubject {
identity: "child2".to_string(),
labels: vec![],
properties: HashMap::new(),
},
elements: vec![],
};
let parent = AstPattern {
subject: AstSubject {
identity: "parent".to_string(),
labels: vec![],
properties: HashMap::new(),
},
elements: vec![child1, child2],
};
assert_eq!(parent.elements.len(), 2);
assert_eq!(parent.elements[0].subject.identity, "child1");
assert_eq!(parent.elements[1].subject.identity, "child2");
let json = serde_json::to_string(&parent).unwrap();
let deserialized: AstPattern = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.elements.len(), 2);
}
#[test]
fn test_from_pattern_simple() {
let subject = Subject {
identity: Symbol("alice".to_string()),
labels: {
let mut labels = HashSet::new();
labels.insert("Person".to_string());
labels
},
properties: HashMap::new(),
};
let pattern = Pattern::point(subject);
let ast = AstPattern::from_pattern(&pattern);
assert_eq!(ast.subject.identity, "alice");
assert_eq!(ast.subject.labels, vec!["Person"]);
assert_eq!(ast.elements.len(), 0);
}
#[test]
fn test_from_pattern_with_properties() {
let subject = Subject {
identity: Symbol("alice".to_string()),
labels: HashSet::new(),
properties: {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::VString("Alice".to_string()));
props.insert("age".to_string(), Value::VInteger(30));
props
},
};
let pattern = Pattern::point(subject);
let ast = AstPattern::from_pattern(&pattern);
assert_eq!(ast.subject.identity, "alice");
assert_eq!(ast.subject.properties.len(), 2);
assert_eq!(ast.subject.properties.get("name").unwrap(), "Alice");
let age_value = ast.subject.properties.get("age").unwrap();
assert_eq!(age_value, 30); }
#[test]
fn test_value_serialization_simple_types() {
let v = value_to_json(&Value::VInteger(42));
assert_eq!(v, serde_json::json!(42));
assert!(v.is_number());
let v = value_to_json(&Value::VDecimal(3.14));
assert_eq!(v, serde_json::json!(3.14));
assert!(v.is_number());
let v = value_to_json(&Value::VBoolean(true));
assert_eq!(v, serde_json::Value::Bool(true));
let v = value_to_json(&Value::VString("hello".to_string()));
assert_eq!(v, serde_json::Value::String("hello".to_string()));
let v = value_to_json(&Value::VArray(vec![Value::VInteger(1), Value::VInteger(2)]));
assert!(v.is_array());
assert_eq!(v.as_array().unwrap().len(), 2);
assert_eq!(v.as_array().unwrap()[0], serde_json::json!(1));
assert_eq!(v.as_array().unwrap()[1], serde_json::json!(2));
}
#[test]
fn test_value_serialization_tagged_types() {
let v = value_to_json(&Value::VSymbol("user123".to_string()));
assert_eq!(v["type"], "symbol");
assert_eq!(v["value"], "user123");
let v = value_to_json(&Value::VRange(pattern_core::RangeValue {
lower: Some(1.0),
upper: Some(10.0),
}));
assert_eq!(v["type"], "range");
assert_eq!(v["lower"], 1.0);
assert_eq!(v["upper"], 10.0);
let v = value_to_json(&Value::VMeasurement {
unit: "cm".to_string(),
value: 168.0,
});
assert_eq!(v["type"], "measurement");
assert_eq!(v["value"], 168.0);
assert_eq!(v["unit"], "cm");
let v = value_to_json(&Value::VTaggedString {
tag: "date".to_string(),
content: "2024-01-09".to_string(),
});
assert_eq!(v["type"], "tagged");
assert_eq!(v["tag"], "date");
assert_eq!(v["content"], "2024-01-09");
}
#[test]
fn test_value_serialization_map() {
let mut map = HashMap::new();
map.insert("key1".to_string(), Value::VString("value1".to_string()));
map.insert("key2".to_string(), Value::VInteger(42));
let v = value_to_json(&Value::VMap(map));
assert!(v.is_object());
assert_eq!(v["key1"], "value1");
assert_eq!(v["key2"], 42);
}
}