use core_api::{default_max_edges, json_to_value, EdgeInfo, NodeInfo, ResultSet, RuleDef, Value};
use serde_json::{json, Value as Js};
use std::collections::BTreeMap;
pub(crate) fn value_to_json(v: &Value) -> Js {
match v {
Value::Int(i) => json!(i),
Value::Float(f) => serde_json::Number::from_f64(*f)
.map(Js::Number)
.unwrap_or(Js::Null),
Value::Str(s) => json!(s),
Value::Bool(b) => json!(b),
Value::List(xs) => Js::Array(xs.iter().map(value_to_json).collect()),
Value::Map(m) => {
let obj: serde_json::Map<String, Js> = m
.iter()
.map(|(k, v)| (k.clone(), value_to_json(v)))
.collect();
Js::Object(obj)
}
}
}
pub(crate) fn node_info_json(info: &NodeInfo) -> Js {
let props: serde_json::Map<String, Js> = info
.props
.iter()
.map(|(k, v)| (k.clone(), value_to_json(v)))
.collect();
json!({
"key": info.key,
"label": info.label,
"props": props,
})
}
pub(crate) fn node_edges_json(edges: &[EdgeInfo]) -> Js {
json!({
"edges": edges.iter().map(|e| json!({
"edge_type": e.edge_type,
"src_key": e.src_key,
"dst_key": e.dst_key,
"derived": e.derived,
})).collect::<Vec<_>>()
})
}
pub(crate) fn result_set_json(rs: &ResultSet) -> Js {
let columns: Vec<&str> = rs.columns().iter().map(String::as_str).collect();
let rows: Vec<Vec<Js>> = (0..rs.len())
.map(|i| {
rs.row(i)
.iter()
.map(|cell| cell.as_ref().map(value_to_json).unwrap_or(Js::Null))
.collect()
})
.collect();
json!({ "columns": columns, "rows": rows })
}
pub(crate) fn params_from_json(v: Option<&Js>) -> Result<BTreeMap<String, Value>, String> {
let Some(v) = v else {
return Ok(BTreeMap::new());
};
let obj = v
.as_object()
.ok_or_else(|| "params must be an object".to_string())?;
let mut out = BTreeMap::new();
for (k, val) in obj {
match json_to_value(val.clone()) {
Some(mapped) => {
out.insert(k.clone(), mapped);
}
None => {
}
}
}
Ok(out)
}
pub(crate) fn parse_ingest_edges(v: &Js) -> Result<Vec<(String, String, String)>, String> {
let arr = v
.as_array()
.ok_or_else(|| "edges must be an array".to_string())?;
let mut out = Vec::with_capacity(arr.len());
for (i, item) in arr.iter().enumerate() {
let obj = item
.as_object()
.ok_or_else(|| format!("edges[{i}] must be an object"))?;
let edge_type = obj
.get("edge_type")
.and_then(Js::as_str)
.ok_or_else(|| format!("edges[{i}] missing edge_type"))?;
let src = obj
.get("src")
.and_then(Js::as_str)
.ok_or_else(|| format!("edges[{i}] missing src"))?;
let dst = obj
.get("dst")
.and_then(Js::as_str)
.ok_or_else(|| format!("edges[{i}] missing dst"))?;
out.push((edge_type.to_string(), src.to_string(), dst.to_string()));
}
Ok(out)
}
pub(crate) fn rule_def_from_json(v: Js) -> Result<RuleDef, String> {
let mut def: RuleDef = serde_json::from_value(v).map_err(|e| e.to_string())?;
if def.max_edges.is_none() {
def.max_edges = Some(default_max_edges(&def.predicate));
}
Ok(def)
}