use crate::error::{Error, Result};
use crate::procedures::{ProcRow, ProcedureRegistry};
use crate::reader::GraphReader;
use crate::value::{ParamMap, Value};
use crate::writer::GraphWriter;
use meshdb_core::Property;
use std::collections::HashMap;
pub fn run_cypher(
reader: &dyn GraphReader,
writer: &dyn GraphWriter,
args: &[Value],
procedures: &ProcedureRegistry,
allow_writes: bool,
) -> Result<Vec<ProcRow>> {
if args.len() != 2 {
return Err(Error::Procedure(format!(
"apoc.cypher.* expects 2 arguments (cypher, params), got {}",
args.len()
)));
}
let cypher = match &args[0] {
Value::Property(Property::String(s)) => s.clone(),
Value::Null | Value::Property(Property::Null) => {
return Err(Error::Procedure(
"apoc.cypher.*: first argument (cypher) must not be null".into(),
));
}
other => {
return Err(Error::Procedure(format!(
"apoc.cypher.*: first argument (cypher) must be a string, got {other:?}"
)));
}
};
let inner_params = params_from_arg(&args[1])?;
let stmt = meshdb_cypher::parse(&cypher)
.map_err(|e| Error::Procedure(format!("apoc.cypher.*: parse error in inner query: {e}")))?;
let plan = meshdb_cypher::plan(&stmt)
.map_err(|e| Error::Procedure(format!("apoc.cypher.*: plan error: {e}")))?;
if !allow_writes && crate::ops::plan_contains_writes(&plan) {
return Err(Error::Procedure(
"apoc.cypher.run cannot execute a query that contains writes — use apoc.cypher.doIt"
.into(),
));
}
let rows = crate::ops::execute_with_reader_and_procs(
&plan,
reader,
writer,
&inner_params,
procedures,
)?;
Ok(rows
.into_iter()
.map(|row| {
let mut out: ProcRow = HashMap::new();
out.insert("value".to_string(), Value::Map(row));
out
})
.collect())
}
fn params_from_arg(v: &Value) -> Result<ParamMap> {
match v {
Value::Null | Value::Property(Property::Null) => Ok(HashMap::new()),
Value::Map(entries) => Ok(entries.clone()),
Value::Property(Property::Map(entries)) => Ok(entries
.iter()
.map(|(k, p)| (k.clone(), Value::Property(p.clone())))
.collect()),
other => Err(Error::Procedure(format!(
"apoc.cypher.*: second argument (params) must be a map, got {other:?}"
))),
}
}