use super::*;
pub(crate) fn subcontext_traced(
body: &Query,
adapter: &impl AstAdapter,
node: NodeId,
trace: &Trace,
outer: Option<&Scope<'_, GraphExt>>,
) -> (Value, Prov) {
let (caps, projected) = eval_query_caps_outer(body, adapter, node, trace, outer);
if projected {
let mut vs: Vec<(Value, Prov)> = caps
.into_iter()
.map(|c| (c.topic.clone().unwrap_or(Value::Null), c.prov))
.collect();
match vs.len() {
0 => (Value::Null, Prov::default()),
1 => vs.pop().unwrap(),
_ => {
let (values, provs): (Vec<Value>, Vec<Prov>) = vs.into_iter().unzip();
(Value::list(values), Prov::list(provs))
}
}
} else {
let mut o = Origins::default();
for c in &caps {
o.insert(Origin {
position: (c.ext.node).into(),
command: command_now(),
});
}
(Value::Int(caps.len() as i64), Prov::leaf(o))
}
}
pub(crate) fn node_scalar(adapter: &impl AstAdapter, node: NodeId) -> Value {
adapter.name(node).map_or(Value::Null, Value::Str)
}
pub(crate) fn rel_nodes(
adapter: &impl AstAdapter,
node: NodeId,
steps: &[PathElem],
anchor: &Anchor,
trace: &Trace,
bound: &[Option<NodeId>],
scope: Scope<'_, GraphExt>,
) -> Vec<NodeId> {
let witness = if bound.is_empty() {
witness_of(&scope)
} else {
bound
};
let mut nodes = Vec::new();
let inner = Scope {
members: &[],
..scope
};
with_enclosing_topic(&scope, || {
for from in operand_from(adapter, anchor, node, trace, bound, scope) {
nodes.extend(navigate_from(
steps,
adapter,
from,
trace,
nested_outer(&inner),
witness,
inner.register,
));
}
});
dedup(nodes)
}
pub(crate) fn operand_nodes(
adapter: &impl AstAdapter,
node: NodeId,
at: &Operand,
trace: &Trace,
bound: &[Option<NodeId>],
scope: Scope<'_, GraphExt>,
) -> Vec<NodeId> {
match at {
Operand::Rel { steps, anchor, .. } => {
rel_nodes(adapter, node, steps, anchor, trace, bound, scope)
}
Operand::Ctx { index, steps, .. } => {
let bound = if bound.is_empty() {
witness_of(&scope)
} else {
bound
};
let base = match index {
Some(k) => bound.get(k.saturating_sub(1)).copied(),
None => Some(Some(node)),
};
let Some(Some(base)) = base else {
return Vec::new();
};
if steps.is_empty() {
vec![base]
} else {
dedup(navigate_from(
steps,
adapter,
base,
trace,
nested_outer(&scope),
bound,
scope.register,
))
}
}
Operand::Invoking(inner) => match scope.outer {
Some(o) => operand_nodes(
adapter,
o.view.node.unwrap_or(node),
inner,
trace,
bound,
*o,
),
None => Vec::new(),
},
Operand::Position(inner) => operand_nodes(adapter, node, inner, trace, bound, scope),
Operand::Driver(inner) => match scope.driver {
Some(d) => operand_nodes(
adapter,
d.view.node.unwrap_or(node),
inner,
trace,
bound,
*d,
),
None => Vec::new(),
},
_ => Vec::new(),
}
}
fn at_witness<'a>(scope: Scope<'a, GraphExt>, bound: &'a [Option<NodeId>]) -> Scope<'a, GraphExt> {
if bound.is_empty() {
scope
} else {
Scope {
witness: Some(bound),
..scope
}
}
}
pub(crate) fn eval_operand_at(
adapter: &impl AstAdapter,
node: NodeId,
operand: &Operand,
trace: &Trace,
bound: &[Option<NodeId>],
scope: Scope<'_, GraphExt>,
) -> Vec<Value> {
let scope = at_witness(scope, bound);
eval_operand(adapter, &GraphExt::at(node), operand, trace, scope)
}
pub(crate) fn operand_value_at(
adapter: &impl AstAdapter,
node: NodeId,
expr: &Operand,
trace: &Trace,
bound: &[Option<NodeId>],
scope: Scope<'_, GraphExt>,
) -> Value {
let scope = at_witness(scope, bound);
operand_value(adapter, &GraphExt::at(node), expr, trace, scope)
}
pub(crate) fn eval_pred_expr_at(
adapter: &impl AstAdapter,
node: NodeId,
e: &PredExpr,
trace: &Trace,
bound: &[Option<NodeId>],
scope: Scope<'_, GraphExt>,
) -> bool {
let scope = at_witness(scope, bound);
eval_pred_expr(adapter, &GraphExt::at(node), e, trace, scope)
}