use std::collections::HashMap;
use crate::types::Value;
#[derive(Debug, Clone, PartialEq)]
pub struct Node {
pub id: i64,
pub labels: Vec<String>,
pub properties: HashMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Edge {
pub id: i64,
pub start_node: i64,
pub end_node: i64,
pub edge_type: String,
pub properties: HashMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Path {
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
}
pub fn as_node(v: &Value) -> Option<Node> {
let obj = v.as_object().ok()?;
if !obj.contains_key("labels") {
return None;
}
if obj.contains_key("start_node") {
return None; }
let id = obj.get("id").and_then(|v| v.as_int().ok()).unwrap_or(0);
let labels = obj
.get("labels")
.and_then(|v| v.as_array().ok())
.map(|arr| {
arr.iter()
.filter_map(|item| item.as_string().ok().map(String::from))
.collect()
})
.unwrap_or_default();
let properties = obj
.get("properties")
.and_then(|v| v.as_object().ok())
.cloned()
.unwrap_or_default();
Some(Node {
id,
labels,
properties,
})
}
pub fn as_edge(v: &Value) -> Option<Edge> {
let obj = v.as_object().ok()?;
if !obj.contains_key("start_node") || !obj.contains_key("end_node") {
return None;
}
let id = obj.get("id").and_then(|v| v.as_int().ok()).unwrap_or(0);
let start_node = obj
.get("start_node")
.and_then(|v| v.as_int().ok())
.unwrap_or(0);
let end_node = obj
.get("end_node")
.and_then(|v| v.as_int().ok())
.unwrap_or(0);
let edge_type = obj
.get("type")
.and_then(|v| v.as_string().ok())
.map(String::from)
.unwrap_or_default();
let properties = obj
.get("properties")
.and_then(|v| v.as_object().ok())
.cloned()
.unwrap_or_default();
Some(Edge {
id,
start_node,
end_node,
edge_type,
properties,
})
}
pub fn as_path(v: &Value) -> Option<Path> {
let obj = v.as_object().ok()?;
if !obj.contains_key("nodes") || !obj.contains_key("edges") {
return None;
}
let nodes = obj
.get("nodes")
.and_then(|v| v.as_array().ok())
.map(|arr| arr.iter().filter_map(as_node).collect())
.unwrap_or_default();
let edges = obj
.get("edges")
.and_then(|v| v.as_array().ok())
.map(|arr| arr.iter().filter_map(as_edge).collect())
.unwrap_or_default();
Some(Path { nodes, edges })
}
#[cfg(test)]
mod tests {
use super::*;
fn node_value(id: i64, labels: &[&str], props: &[(&str, Value)]) -> Value {
let mut obj = HashMap::new();
obj.insert("id".to_string(), Value::int(id));
obj.insert(
"labels".to_string(),
Value::array(labels.iter().map(|l| Value::string(*l)).collect()),
);
let mut p = HashMap::new();
for (k, v) in props {
p.insert(k.to_string(), v.clone());
}
obj.insert("properties".to_string(), Value::object(p));
Value::object(obj)
}
fn edge_value(id: i64, start: i64, end: i64, typ: &str) -> Value {
let mut obj = HashMap::new();
obj.insert("id".to_string(), Value::int(id));
obj.insert("start_node".to_string(), Value::int(start));
obj.insert("end_node".to_string(), Value::int(end));
obj.insert("type".to_string(), Value::string(typ));
obj.insert("properties".to_string(), Value::object(HashMap::new()));
Value::object(obj)
}
#[test]
fn test_as_node_from_object() {
let v = node_value(42, &["Person", "User"], &[("name", Value::string("Alice"))]);
let node = as_node(&v).expect("should decode node");
assert_eq!(node.id, 42);
assert_eq!(node.labels, vec!["Person".to_string(), "User".to_string()]);
assert_eq!(
node.properties.get("name").unwrap().as_string().unwrap(),
"Alice"
);
}
#[test]
fn test_as_edge_from_object() {
let v = edge_value(100, 1, 2, "KNOWS");
let edge = as_edge(&v).expect("should decode edge");
assert_eq!(edge.id, 100);
assert_eq!(edge.start_node, 1);
assert_eq!(edge.end_node, 2);
assert_eq!(edge.edge_type, "KNOWS");
assert!(edge.properties.is_empty());
}
#[test]
fn test_as_path_from_object() {
let n1 = node_value(1, &["Person"], &[]);
let n2 = node_value(2, &["Person"], &[]);
let e1 = edge_value(10, 1, 2, "KNOWS");
let mut obj = HashMap::new();
obj.insert("nodes".to_string(), Value::array(vec![n1, n2]));
obj.insert("edges".to_string(), Value::array(vec![e1]));
let v = Value::object(obj);
let path = as_path(&v).expect("should decode path");
assert_eq!(path.nodes.len(), 2);
assert_eq!(path.edges.len(), 1);
assert_eq!(path.nodes[0].id, 1);
assert_eq!(path.nodes[1].id, 2);
assert_eq!(path.edges[0].edge_type, "KNOWS");
}
#[test]
fn test_as_node_rejects_scalar() {
assert!(as_node(&Value::int(5)).is_none());
assert!(as_node(&Value::string("x")).is_none());
assert!(as_node(&Value::null()).is_none());
assert!(as_node(&edge_value(1, 2, 3, "T")).is_none());
}
#[test]
fn test_as_edge_rejects_node() {
let v = node_value(1, &["Person"], &[]);
assert!(as_edge(&v).is_none());
assert!(as_edge(&Value::int(5)).is_none());
}
#[test]
fn test_as_path_rejects_non_path() {
assert!(as_path(&node_value(1, &["Person"], &[])).is_none());
assert!(as_path(&Value::int(5)).is_none());
}
}