use lora_store::GraphStorage;
use super::super::expr::EvalContext;
use crate::value::LoraValue;
pub(super) fn dispatch<S: GraphStorage>(
op: &str,
args: &[LoraValue],
ctx: &EvalContext<'_, S>,
) -> Option<LoraValue> {
Some(match op {
"size" => size(args),
"keys" => keys(args, ctx),
"properties" => properties(args, ctx),
"reverse" => reverse(args),
"coalesce" | "first_non_null" => coalesce(args),
"is_null" => is_null(args),
"is_not_null" => is_not_null(args),
"id" => id(args),
_ => return None,
})
}
#[cfg(test)]
pub(super) fn known(op: &str) -> Option<()> {
matches!(
op,
"size" | "keys" | "properties" | "reverse" | "coalesce" | "is_null" | "is_not_null" | "id"
)
.then_some(())
}
fn id(args: &[LoraValue]) -> LoraValue {
match args.first() {
Some(LoraValue::Node(id)) => LoraValue::Int(*id as i64),
Some(LoraValue::Relationship(id)) => LoraValue::Int(*id as i64),
_ => LoraValue::Null,
}
}
fn size(args: &[LoraValue]) -> LoraValue {
match args.first() {
Some(LoraValue::List(l)) => LoraValue::Int(l.len() as i64),
Some(LoraValue::String(s)) => LoraValue::Int(s.chars().count() as i64),
Some(LoraValue::Map(m)) => LoraValue::Int(m.len() as i64),
Some(LoraValue::Path(p)) => LoraValue::Int(p.rels.len() as i64),
Some(LoraValue::Vector(v)) => LoraValue::Int(v.dimension as i64),
Some(LoraValue::Binary(b)) => LoraValue::Int(b.len() as i64),
_ => LoraValue::Null,
}
}
fn keys<S: GraphStorage>(args: &[LoraValue], ctx: &EvalContext<'_, S>) -> LoraValue {
match args.first() {
Some(LoraValue::Map(m)) => {
LoraValue::List(m.keys().cloned().map(LoraValue::String).collect())
}
Some(LoraValue::Node(id)) => ctx
.storage
.with_node(*id, |n| {
LoraValue::List(
n.properties
.keys()
.map(|k| LoraValue::String(k.to_string()))
.collect(),
)
})
.unwrap_or(LoraValue::Null),
Some(LoraValue::Relationship(id)) => ctx
.storage
.with_relationship(*id, |r| {
LoraValue::List(
r.properties
.keys()
.map(|k| LoraValue::String(k.to_string()))
.collect(),
)
})
.unwrap_or(LoraValue::Null),
_ => LoraValue::Null,
}
}
fn properties<S: GraphStorage>(args: &[LoraValue], ctx: &EvalContext<'_, S>) -> LoraValue {
match args.first() {
Some(LoraValue::Map(m)) => LoraValue::Map(m.clone()),
Some(LoraValue::Node(id)) => ctx
.storage
.with_node(*id, |n| {
LoraValue::Map(
n.properties
.iter()
.map(|(k, v)| (k.to_string(), LoraValue::from(v)))
.collect(),
)
})
.unwrap_or(LoraValue::Null),
Some(LoraValue::Relationship(id)) => ctx
.storage
.with_relationship(*id, |r| {
LoraValue::Map(
r.properties
.iter()
.map(|(k, v)| (k.to_string(), LoraValue::from(v)))
.collect(),
)
})
.unwrap_or(LoraValue::Null),
_ => LoraValue::Null,
}
}
fn reverse(args: &[LoraValue]) -> LoraValue {
match args.first() {
Some(LoraValue::List(l)) => LoraValue::List(l.iter().rev().cloned().collect()),
Some(LoraValue::String(s)) => LoraValue::String(s.chars().rev().collect()),
_ => LoraValue::Null,
}
}
fn coalesce(args: &[LoraValue]) -> LoraValue {
for arg in args {
if !matches!(arg, LoraValue::Null) {
return arg.clone();
}
}
LoraValue::Null
}
fn is_null(args: &[LoraValue]) -> LoraValue {
match args.first() {
Some(LoraValue::Null) | None => LoraValue::Bool(true),
Some(_) => LoraValue::Bool(false),
}
}
fn is_not_null(args: &[LoraValue]) -> LoraValue {
match args.first() {
Some(LoraValue::Null) | None => LoraValue::Bool(false),
Some(_) => LoraValue::Bool(true),
}
}