mod parse;
pub use parse::{
AliasDecl, AliasKind, AliasRule, EdgeDecl, Model, Mount, NodeDecl, RefDecl, RelDecl,
parse_model, parse_model_file, resolve_mount_target,
};
use quarb::{AstAdapter, NodeId, QueryResult, Value};
use std::cell::{Cell, OnceCell, RefCell};
use std::collections::HashMap;
const MODEL_TAG: u64 = 1 << 63;
const CIDX_SHIFT: u64 = 44;
const VAL_MASK: u64 = (1 << CIDX_SHIFT) - 1;
#[derive(Clone)]
enum Member {
Value(Value),
Node(NodeId),
}
struct Container {
name: String,
members: Vec<Member>,
roles: Vec<(String, String)>,
by_str: HashMap<String, usize>,
by_role: HashMap<String, HashMap<String, usize>>,
}
impl Container {
fn member(&self, v: usize) -> Option<Member> {
if v == 0 {
return None;
}
self.members.get(v - 1).cloned()
}
fn role_of(&self, v: usize) -> &str {
&self.roles[v - 1].0
}
fn trait_of(&self, v: usize) -> &str {
&self.roles[v - 1].1
}
fn ordinal_in_role(&self, v: usize) -> usize {
let role = &self.roles[v - 1].0;
self.roles[..v - 1]
.iter()
.filter(|(r, _)| r == role)
.count()
+ 1
}
fn of_role<'a>(&'a self, role: &'a str) -> impl Iterator<Item = usize> + 'a {
self.roles
.iter()
.enumerate()
.filter(move |(_, (r, _))| r == role)
.map(|(i, _)| i)
}
}
struct Fabric {
alias: HashMap<NodeId, (NodeId, String)>,
resolve: HashMap<(NodeId, String), NodeId>,
sources: HashMap<(NodeId, String), Vec<NodeId>>,
ref_back: HashMap<NodeId, Vec<(String, NodeId)>>,
ref_fwd: HashMap<NodeId, Vec<(String, NodeId)>>,
rel_fwd: HashMap<NodeId, Vec<(String, NodeId)>>,
rel_back: HashMap<NodeId, Vec<(String, NodeId)>>,
edges: HashMap<NodeId, Vec<(String, NodeId)>>,
}
pub struct ModelAdapter<A: AstAdapter> {
base: A,
model: Model,
containers: OnceCell<Vec<Container>>,
fabric: OnceCell<Fabric>,
bodies: OnceCell<Vec<quarb::StepBody>>,
decided: RefCell<HashMap<(usize, NodeId), bool>>,
visible: Cell<usize>,
}
impl<A: AstAdapter> ModelAdapter<A> {
pub fn new(base: A, model: Model) -> Self {
let n = model.traits.len();
ModelAdapter {
base,
model,
containers: OnceCell::new(),
fabric: OnceCell::new(),
bodies: OnceCell::new(),
decided: RefCell::new(HashMap::new()),
visible: Cell::new(n),
}
}
pub fn check_declared_traits(&self) -> Result<(), String> {
for decl in &self.model.traits {
if let Some(n) = self.model.nodes.iter().find(|n| n.trait_name == decl.name) {
return Err(format!(
"trait <{}> is already the trait of the derived container /{} — \
a declared trait cannot redefine one the arbor has",
decl.name, n.name
));
}
let q = format!("*//*<{}> @| count", decl.name);
if let Ok(QueryResult::Values(vs)) = quarb::run(&q, &self.base)
&& matches!(vs.first(), Some(Value::Int(n)) if *n > 0)
{
return Err(format!(
"trait <{}> is the adapter's own — a declared trait cannot \
redefine one the arbor has",
decl.name
));
}
}
Ok(())
}
fn declared_traits_of(&self, node: NodeId) -> Vec<String> {
let mut out = Vec::new();
for i in 0..self.visible.get().min(self.model.traits.len()) {
if self.declared_at(i, node) {
out.push(self.model.traits[i].name.clone());
}
}
out
}
fn declared_at(&self, i: usize, node: NodeId) -> bool {
if let Some(&d) = self.decided.borrow().get(&(i, node)) {
return d;
}
let bodies = self.bodies.get_or_init(|| {
self.model
.traits
.iter()
.map(|t| {
quarb::parse_step_body(&t.step).expect("validated when the model was parsed")
})
.collect()
});
let was = self.visible.get();
self.visible.set(i);
let d = quarb::node_matches_step_body(self, node, &bodies[i]);
self.visible.set(was);
self.decided.borrow_mut().insert((i, node), d);
d
}
pub fn base(&self) -> &A {
&self.base
}
pub fn locator(&self, node: NodeId, base_locator: impl Fn(NodeId) -> String) -> String {
match self.decode(node) {
None => base_locator(node),
Some((c, 0)) => format!("/{}", self.containers()[c].name),
Some((c, v)) => {
let cont = &self.containers()[c];
format!(
"/{}/{}[{}]",
cont.name,
cont.role_of(v),
cont.ordinal_in_role(v)
)
}
}
}
fn seeded_defs(&self) -> quarb::Defs {
quarb::parse_defs(&self.model.defs_text).unwrap_or_default()
}
fn containers(&self) -> &[Container] {
self.containers.get_or_init(|| {
let defs = self.seeded_defs();
let mut built: Vec<Container> = Vec::new();
for decl in &self.model.nodes {
let members = self.members(&decl.query, &defs, &built);
let idx = match built.iter().position(|c| c.name == decl.name) {
Some(i) => i,
None => {
built.push(Container {
name: decl.name.clone(),
members: Vec::new(),
roles: Vec::new(),
by_str: HashMap::new(),
by_role: HashMap::new(),
});
built.len() - 1
}
};
let keyed: Vec<(Member, Option<String>)> = members
.into_iter()
.map(|m| {
let key = match &m {
Member::Value(v) => Some(v.to_string()),
Member::Node(n) => self.base_key(*n),
};
(m, key)
})
.collect();
let cont = &mut built[idx];
let held_nodes: std::collections::HashSet<NodeId> = cont
.members
.iter()
.zip(&cont.roles)
.filter(|(_, (r, _))| *r == decl.role)
.filter_map(|(m, _)| match m {
Member::Node(n) => Some(*n),
Member::Value(_) => None,
})
.collect();
let by_role = cont.by_role.entry(decl.role.clone()).or_default();
for (m, key) in keyed {
let held = match &m {
Member::Node(n) => held_nodes.contains(n),
Member::Value(_) => key.as_deref().is_some_and(|k| by_role.contains_key(k)),
};
if held {
continue;
}
let i = cont.members.len();
cont.members.push(m);
cont.roles
.push((decl.role.clone(), decl.trait_name.clone()));
if let Some(k) = key {
by_role.entry(k.clone()).or_insert(i);
cont.by_str.entry(k).or_insert(i);
}
}
}
built
})
}
fn members(&self, query: &str, defs: &quarb::Defs, prior: &[Container]) -> Vec<Member> {
let result = if prior.is_empty() {
quarb::run_with_defs(query, defs, &self.base)
} else {
let scratch = PriorView {
base: &self.base,
prior,
};
quarb::run_with_defs(query, defs, &scratch)
};
match result {
Ok(QueryResult::Values(vs)) => {
let mut seen = std::collections::HashSet::new();
vs.into_iter()
.filter(|v| seen.insert(v.to_string()))
.map(Member::Value)
.collect()
}
Ok(QueryResult::Nodes(ns)) => {
let mut seen = std::collections::HashSet::new();
ns.into_iter()
.filter(|n| seen.insert(*n))
.map(Member::Node)
.collect()
}
Err(_) => Vec::new(),
}
}
fn base_key(&self, node: NodeId) -> Option<String> {
self.base
.default_value(node)
.map(|v| v.to_string())
.or_else(|| self.base.name(node))
}
fn aliased(&self, node: NodeId) -> Option<NodeId> {
match self.decode(node) {
Some((c, v)) if v > 0 => match self.containers()[c].member(v)? {
Member::Node(n) => Some(n),
Member::Value(_) => None,
},
_ => None,
}
}
fn role_at(&self, node: NodeId) -> Option<String> {
match self.decode(node) {
Some((c, v)) if v > 0 => Some(self.containers()[c].role_of(v).to_string()),
_ => None,
}
}
fn elevated(&self, node: NodeId) -> Option<Value> {
match self.decode(node) {
Some((c, v)) if v > 0 => match self.containers()[c].member(v)? {
Member::Value(val) => Some(val),
Member::Node(_) => None,
},
_ => None,
}
}
fn container_node(c: usize) -> NodeId {
NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT)
}
fn value_node(c: usize, v: usize) -> NodeId {
NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (v as u64 + 1))
}
fn decode(&self, node: NodeId) -> Option<(usize, usize)> {
if node.0 & MODEL_TAG == 0 {
return None;
}
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
(c < self.containers().len()).then_some((c, v))
}
fn container_by_name(&self, name: &str) -> Option<usize> {
self.containers().iter().position(|c| c.name == name)
}
fn fabric(&self) -> &Fabric {
self.fabric.get_or_init(|| {
let defs = self.seeded_defs();
let mut f = Fabric {
alias: HashMap::new(),
resolve: HashMap::new(),
sources: HashMap::new(),
ref_back: HashMap::new(),
ref_fwd: HashMap::new(),
rel_fwd: HashMap::new(),
rel_back: HashMap::new(),
edges: HashMap::new(),
};
for (c, cont) in self.containers().iter().enumerate() {
for (i, m) in cont.members.iter().enumerate() {
if let Member::Node(n) = m {
f.alias
.entry(*n)
.or_insert((Self::value_node(c, i), cont.roles[i].0.clone()));
}
}
}
let mut reached: HashMap<(NodeId, String), Vec<(String, NodeId)>> = HashMap::new();
for decl in &self.model.refs {
let mut index: HashMap<String, NodeId> = HashMap::new();
let derived = self.container_by_name(&decl.container);
match derived {
Some(container) => {
let cont = &self.containers()[container];
let slots: Vec<usize> = match decl.role.as_deref() {
Some(role) => cont.of_role(role).map(|i| i + 1).collect(),
None => (1..=cont.members.len()).collect(),
};
for v in slots {
let n = Self::value_node(container, v - 1);
let key = match decl.key_field.as_deref() {
Some(f) => self.property(n, f).map(|k| k.to_string()),
None => match cont.member(v) {
Some(Member::Value(val)) => Some(val.to_string()),
Some(Member::Node(b)) => self.base_key(b),
None => None,
},
};
if let Some(k) = key {
index.entry(k).or_insert(n);
}
}
}
None => {
let view = PriorView {
base: &self.base,
prior: self.containers(),
};
for n in self.view_nodes(&decl.path, &defs) {
let key = match decl.key_field.as_deref() {
Some(f) => view.property(n, f).map(|k| k.to_string()),
None => view
.default_value(n)
.map(|v| v.to_string())
.or_else(|| view.name(n)),
};
if let Some(k) = key {
index.entry(k).or_insert(n);
}
}
}
}
let path_label = scope_role(&decl.path);
let mut scoped = self.scope_nodes(&decl.scope, &defs);
if scoped.is_empty() {
scoped = self
.view_nodes(&decl.scope, &defs)
.into_iter()
.filter_map(|n| self.aliased(n))
.collect();
}
for node in scoped {
let Some(value) = self.base.property(node, &decl.field) else {
continue;
};
if matches!(value, Value::Null) {
continue;
}
let elements: Vec<Value> = match value {
l @ Value::List(_) => l.into_items().unwrap_or_default(),
other => vec![other],
};
let mut first = true;
for value in elements {
let Some(target) = index.get(&value.to_string()).copied() else {
continue;
};
let fwd = self.role_at(target).unwrap_or_else(|| path_label.clone());
let (back_node, back) = match f.alias.get(&node) {
Some((alias, role)) => (*alias, role.clone()),
None => (node, scope_role(&decl.scope)),
};
if first {
f.resolve.insert((node, decl.field.clone()), target);
first = false;
}
reached
.entry((node, decl.field.clone()))
.or_default()
.push((fwd.clone(), target));
let from = f.sources.entry((target, decl.field.clone())).or_default();
if !from.contains(&back_node) {
from.push(back_node);
}
f.ref_fwd.entry(node).or_default().push((fwd, target));
f.ref_back
.entry(target)
.or_default()
.push((back, back_node));
}
}
}
for decl in &self.model.rels {
let fwd = scope_role(&decl.target);
let back = scope_role(&decl.source);
for source in self.view_nodes(&decl.source, &defs) {
let cond = self.bind_driver(&decl.cond, source);
let query = format!("{}{}", decl.target, cond);
for target in self.view_nodes(&query, &defs) {
if target == source {
continue;
}
f.rel_fwd
.entry(source)
.or_default()
.push((fwd.clone(), target));
f.rel_back
.entry(target)
.or_default()
.push((back.clone(), source));
}
}
}
for decl in &self.model.edges {
let mut seen = std::collections::HashSet::new();
for node in self.scope_nodes(&decl.scope, &defs) {
let (Some(ends_a), Some(ends_b)) = (
reached.get(&(node, decl.field_a.clone())),
reached.get(&(node, decl.field_b.clone())),
) else {
continue;
};
for (ra, na) in ends_a {
for (rb, nb) in ends_b {
if na != nb && seen.insert((*na, *nb)) {
f.edges.entry(*na).or_default().push((rb.clone(), *nb));
f.edges.entry(*nb).or_default().push((ra.clone(), *na));
}
}
}
}
}
f
})
}
fn scope_nodes(&self, scope: &str, defs: &quarb::Defs) -> Vec<NodeId> {
match quarb::run_with_defs(scope, defs, &self.base) {
Ok(QueryResult::Nodes(ns)) => ns,
_ => Vec::new(),
}
}
fn view_nodes(&self, path: &str, defs: &quarb::Defs) -> Vec<NodeId> {
let view = PriorView {
base: &self.base,
prior: self.containers(),
};
match quarb::run_with_defs(path, defs, &view) {
Ok(QueryResult::Nodes(ns)) => ns,
_ => Vec::new(),
}
}
fn bind_driver(&self, cond: &str, source: NodeId) -> String {
let view = PriorView {
base: &self.base,
prior: self.containers(),
};
let b: Vec<char> = cond.chars().collect();
let mut out = String::new();
let mut i = 0;
while i < b.len() {
match b[i] {
q @ ('\'' | '"') => {
out.push(q);
i += 1;
while i < b.len() && b[i] != q {
if b[i] == '\\' && i + 1 < b.len() {
out.push(b[i]);
i += 1;
}
out.push(b[i]);
i += 1;
}
if i < b.len() {
out.push(b[i]);
i += 1;
}
}
c @ ('$' | '_')
if (c == '$' && b.get(i + 1) == Some(&'$'))
|| (c == '_'
&& !(i > 0
&& (b[i - 1].is_alphanumeric()
|| b[i - 1] == '_'
|| b[i - 1] == '-'))
&& !b.get(i + 1).is_some_and(|c| {
c.is_alphanumeric() || *c == '_' || *c == '-'
})) =>
{
if c == '$' {
i += 2;
if b.get(i) == Some(&'_')
&& !b
.get(i + 1)
.is_some_and(|c| c.is_alphanumeric() || *c == '_' || *c == '-')
{
i += 1;
}
} else {
i += 1;
}
let value = if b.get(i) == Some(&':') && b.get(i + 1) == Some(&':') {
i += 2;
let start = i;
while i < b.len() && (b[i].is_alphanumeric() || b[i] == '_' || b[i] == '-')
{
i += 1;
}
let field: String = b[start..i].iter().collect();
view.property(source, &field)
} else {
view.default_value(source)
};
out.push_str(&literal(value.unwrap_or(Value::Null)));
}
c => {
out.push(c);
i += 1;
}
}
}
out
}
}
fn literal(v: Value) -> String {
match v {
Value::Int(n) => n.to_string(),
Value::Float(f) => f.to_string(),
Value::Bool(b) => b.to_string(),
Value::Null => "''".to_string(),
other => format!(
"'{}'",
other.to_string().replace('\\', "\\\\").replace('\'', "\\'")
),
}
}
struct PriorView<'a, A: AstAdapter> {
base: &'a A,
prior: &'a [Container],
}
impl<A: AstAdapter> AstAdapter for PriorView<'_, A> {
fn root(&self) -> NodeId {
self.base.root()
}
fn children(&self, node: NodeId) -> Vec<NodeId> {
if node.0 & MODEL_TAG != 0 {
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
if v == 0 && c < self.prior.len() {
return (0..self.prior[c].members.len())
.map(|i| NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (i as u64 + 1)))
.collect();
}
return Vec::new();
}
let mut kids = self.base.children(node);
if node == self.base.root() {
for c in 0..self.prior.len() {
kids.push(NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT));
}
}
kids
}
fn name(&self, node: NodeId) -> Option<String> {
if node.0 & MODEL_TAG != 0 {
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
let cont = self.prior.get(c)?;
return Some(if v == 0 {
cont.name.clone()
} else {
cont.role_of(v).to_string()
});
}
self.base.name(node)
}
fn child_host(&self, node: NodeId) -> NodeId {
if node.0 & MODEL_TAG != 0 {
node
} else {
self.base.child_host(node)
}
}
fn non_tree_nodes(&self) -> Vec<NodeId> {
self.base.non_tree_nodes()
}
fn properties(&self, node: NodeId) -> Vec<(String, Value)> {
if node.0 & MODEL_TAG != 0 {
Vec::new()
} else {
self.base.properties(node)
}
}
fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
if node == self.base.root() {
let mut out = self.base.children_named(node, name);
if let Some(c) = self.prior.iter().position(|k| k.name == name) {
out.push(NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT));
}
return out;
}
if node.0 & MODEL_TAG != 0 {
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
let Some(cont) = self.prior.get(c) else {
return Vec::new();
};
if v == 0 {
return cont
.of_role(name)
.map(|i| NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (i as u64 + 1)))
.collect();
}
return Vec::new();
}
self.base.children_named(node, name)
}
fn traits(&self, node: NodeId) -> Vec<String> {
if node.0 & MODEL_TAG != 0 {
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
return match self.prior.get(c) {
Some(cont) if v > 0 => vec![cont.trait_of(v).to_string()],
_ => Vec::new(),
};
}
self.base.traits(node)
}
fn parent(&self, node: NodeId) -> Option<NodeId> {
if node.0 & MODEL_TAG != 0 {
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
return Some(if v == 0 {
self.base.root()
} else {
NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT)
});
}
self.base.parent(node)
}
fn property(&self, node: NodeId, name: &str) -> Option<Value> {
if node.0 & MODEL_TAG != 0 {
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
return match self.prior.get(c)?.member(v)? {
Member::Value(_) => None,
Member::Node(n) => self.base.property(n, name),
};
}
self.base.property(node, name)
}
fn default_value(&self, node: NodeId) -> Option<Value> {
if node.0 & MODEL_TAG != 0 {
let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
let v = (node.0 & VAL_MASK) as usize;
return match self.prior.get(c)?.member(v)? {
Member::Value(val) => Some(val),
Member::Node(n) => self.base.default_value(n),
};
}
self.base.default_value(node)
}
fn document_by_ref(&self, id: &str) -> Option<NodeId> {
self.base.document_by_ref(id)
}
fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
if node.0 & MODEL_TAG != 0 || node == self.base.root() {
return None;
}
self.base.descendants_named(node, name)
}
fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
let base: Vec<NodeId> = nodes
.iter()
.copied()
.filter(|n| n.0 & MODEL_TAG == 0)
.collect();
if !base.is_empty() {
self.base.prefetch_links(&base, dir);
}
}
fn provenance(&self, node: NodeId) -> quarb::Provenance {
if node.0 & MODEL_TAG != 0 {
return quarb::Provenance::default();
}
self.base.provenance(node)
}
fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
if node.0 & MODEL_TAG != 0 {
return quarb::ProvenanceList::default();
}
self.base.provenance_list(node)
}
}
impl<A: AstAdapter> AstAdapter for ModelAdapter<A> {
fn root(&self) -> NodeId {
self.base.root()
}
fn children(&self, node: NodeId) -> Vec<NodeId> {
match self.decode(node) {
Some((c, 0)) => (0..self.containers()[c].members.len())
.map(|v| Self::value_node(c, v))
.collect(),
Some(_) => match self.aliased(node) {
Some(base) => self.base.children(base),
None => Vec::new(),
},
None => {
let mut kids = self.base.children(node);
if node == self.base.root() {
for c in 0..self.containers().len() {
kids.push(Self::container_node(c));
}
}
kids
}
}
}
fn child_host(&self, node: NodeId) -> NodeId {
match self.decode(node) {
Some(_) => node,
None => self.base.child_host(node),
}
}
fn non_tree_nodes(&self) -> Vec<NodeId> {
self.base.non_tree_nodes()
}
fn properties(&self, node: NodeId) -> Vec<(String, Value)> {
if node.0 & MODEL_TAG != 0 {
Vec::new()
} else {
self.base.properties(node)
}
}
fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
if node == self.base.root() {
let mut out = self.base.children_named(node, name);
if let Some(c) = self.container_by_name(name) {
out.push(Self::container_node(c));
}
return out;
}
match self.decode(node) {
Some((c, 0)) => self.containers()[c]
.of_role(name)
.map(|i| Self::value_node(c, i))
.collect(),
Some(_) => match self.aliased(node) {
Some(base) => self.base.children_named(base, name),
None => Vec::new(),
},
None => self.base.children_named(node, name),
}
}
fn name(&self, node: NodeId) -> Option<String> {
match self.decode(node) {
Some((c, 0)) => Some(self.containers()[c].name.clone()),
Some((c, v)) => Some(self.containers()[c].role_of(v).to_string()),
None => self.base.name(node),
}
}
fn parent(&self, node: NodeId) -> Option<NodeId> {
match self.decode(node) {
Some((_, 0)) => Some(self.base.root()),
Some((c, _)) => Some(Self::container_node(c)),
None => self.base.parent(node),
}
}
fn traits(&self, node: NodeId) -> Vec<String> {
let mut out = match self.decode(node) {
Some((c, v)) if v > 0 => {
let mut out = vec![self.containers()[c].trait_of(v).to_string()];
if let Some(base) = self.aliased(node) {
out.extend(self.base.traits(base));
}
out
}
Some(_) => Vec::new(),
None => self.base.traits(node),
};
if !self.model.traits.is_empty() {
out.extend(self.declared_traits_of(node));
}
out
}
fn property(&self, node: NodeId, name: &str) -> Option<Value> {
match self.decode(node) {
Some((_, v)) if v > 0 => match self.aliased(node) {
Some(base) => self.base.property(base, name),
None => None,
},
Some(_) => None,
None => self.base.property(node, name),
}
}
fn default_value(&self, node: NodeId) -> Option<Value> {
match self.decode(node) {
Some((_, v)) if v > 0 => match self.aliased(node) {
Some(base) => self.base.default_value(base),
None => self.elevated(node),
},
Some(_) => None,
None => self.base.default_value(node),
}
}
fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
match self.decode(node) {
Some((c, 0)) if key == "n-rows" => {
Some(Value::Int(self.containers()[c].members.len() as i64))
}
Some(_) => None,
None => self.base.metadata(node, key),
}
}
fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
self.base.aliased_metadata(node)
}
fn provenance(&self, node: NodeId) -> quarb::Provenance {
match self.decode(node) {
Some((c, _)) => match self.aliased(node) {
Some(base) => self.base.provenance(base),
None => quarb::Provenance {
source: Some(format!("model:/{}", self.containers()[c].name)),
..Default::default()
},
},
None => self.base.provenance(node),
}
}
fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
match self.decode(node) {
Some((_, _)) => match self.aliased(node) {
Some(base) => self.base.provenance_list(base),
None => quarb::ProvenanceList::single(self.provenance(node)),
},
None => self.base.provenance_list(node),
}
}
fn answers_to(&self, node: NodeId, name: &str) -> bool {
if self.decode(node).is_none() && self.base.answers_to(node, name) {
return true;
}
let Some(canonical) = self.name(node) else {
return canonical_none(name);
};
if canonical == name {
return true;
}
let aliases = self
.model
.aliases
.iter()
.filter(|a| a.kind == AliasKind::Name);
for a in aliases {
if a.admits(name, &canonical) {
return true;
}
if self.decode(node).is_none()
&& let AliasRule::Exact { alias, original } = &a.rule
&& alias == name
&& self.base.answers_to(node, original)
{
return true;
}
}
false
}
fn has_trait(&self, node: NodeId, name: &str) -> bool {
let traits = self.traits(node);
if traits.iter().any(|t| t == name) {
return true;
}
self.model
.aliases
.iter()
.filter(|a| a.kind == AliasKind::Trait)
.any(|a| traits.iter().any(|t| a.admits(name, t)))
}
fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
let under = self.aliased(node).unwrap_or(node);
if let Some(&target) = self.fabric().resolve.get(&(under, property.to_string())) {
return Some(target);
}
if self.decode(under).is_none() {
return self.base.resolve(under, property, hint);
}
None
}
fn external_ref(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<String> {
let under = self.aliased(node).unwrap_or(node);
if self.decode(under).is_none() {
return self.base.external_ref(under, property, hint);
}
None
}
fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
let under = self.aliased(node).unwrap_or(node);
if self.decode(under).is_none() {
return self.base.resolve_fragment(under, fragment);
}
None
}
fn ref_property(&self, node: NodeId) -> Option<String> {
let under = self.aliased(node).unwrap_or(node);
if self.decode(under).is_none() {
return self.base.ref_property(under);
}
None
}
fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
let under = self.aliased(node).unwrap_or(node);
if self.decode(under).is_none() {
return self.base.ref_label(under, property);
}
None
}
fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
let f = self.fabric();
match self.decode(node) {
Some((_, v)) if v > 0 => {
let mut out = f.edges.get(&node).cloned().unwrap_or_default();
if let Some(rels) = f.rel_fwd.get(&node) {
out.extend(rels.iter().cloned());
}
if let Some(base) = self.aliased(node) {
out.extend(self.base.links(base));
if let Some(refs) = f.ref_fwd.get(&base) {
out.extend(refs.iter().cloned());
}
}
out
}
Some(_) => Vec::new(),
None => {
let mut out = self.base.links(node);
if let Some(refs) = f.ref_fwd.get(&node) {
out.extend(refs.iter().cloned());
}
if let Some(rels) = f.rel_fwd.get(&node) {
out.extend(rels.iter().cloned());
}
out
}
}
}
fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
let f = self.fabric();
match self.decode(node) {
Some((_, v)) if v > 0 => {
let mut out = f.ref_back.get(&node).cloned().unwrap_or_default();
if let Some(e) = f.edges.get(&node) {
out.extend(e.iter().cloned());
}
if let Some(rels) = f.rel_back.get(&node) {
out.extend(rels.iter().cloned());
}
out
}
Some(_) => Vec::new(),
None => {
let mut out = self.base.backlinks(node);
if let Some(refs) = f.ref_back.get(&node) {
out.extend(refs.iter().cloned());
}
if let Some(rels) = f.rel_back.get(&node) {
out.extend(rels.iter().cloned());
}
out
}
}
}
fn document_by_ref(&self, id: &str) -> Option<NodeId> {
self.base.document_by_ref(id)
}
fn reverse_resolve(
&self,
node: NodeId,
property: Option<&str>,
hint: Option<&str>,
) -> Option<Vec<NodeId>> {
if let Some(field) = property
&& self.model.refs.iter().any(|r| r.field == field)
{
let f = self.fabric();
let target = f.alias.get(&node).map_or(node, |(alias, _)| *alias);
let mut out = f
.sources
.get(&(target, field.to_string()))
.cloned()
.unwrap_or_default();
if target != node
&& let Some(more) = f.sources.get(&(node, field.to_string()))
{
for n in more {
if !out.contains(n) {
out.push(*n);
}
}
}
return Some(out);
}
if self.decode(node).is_some()
|| !self.model.refs.is_empty()
|| !self.model.rels.is_empty()
|| !self.model.edges.is_empty()
{
return None;
}
self.base.reverse_resolve(node, property, hint)
}
fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
if self.decode(node).is_some()
|| node == self.base.root()
|| self.model.aliases.iter().any(|a| a.kind == AliasKind::Name)
{
return None;
}
self.base.descendants_named(node, name)
}
fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
let base: Vec<NodeId> = nodes
.iter()
.copied()
.filter(|n| self.decode(*n).is_none())
.collect();
if !base.is_empty() {
self.base.prefetch_links(&base, dir);
}
}
fn quantifier_bound(&self) -> usize {
self.base.quantifier_bound()
}
fn allow_shell(&self) -> bool {
self.base.allow_shell()
}
fn invocation_instant(&self) -> Option<(i64, u32)> {
self.base.invocation_instant()
}
fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
self.base.unit_scale(expr)
}
}
fn canonical_none(_name: &str) -> bool {
false
}
fn scope_role(scope: &str) -> String {
scope
.split('/')
.filter(|seg| {
!seg.is_empty()
&& !seg.starts_with('*')
&& !seg.starts_with('[')
&& !seg.chars().next().is_some_and(|c| c.is_ascii_digit())
})
.next_back()
.unwrap_or("")
.split(['[', ':'])
.next()
.unwrap_or("")
.to_string()
}
pub struct Borrowed<'a>(pub &'a dyn AstAdapter);
impl AstAdapter for Borrowed<'_> {
fn root(&self) -> NodeId {
self.0.root()
}
fn children(&self, n: NodeId) -> Vec<NodeId> {
self.0.children(n)
}
fn name(&self, n: NodeId) -> Option<String> {
self.0.name(n)
}
fn answers_to(&self, n: NodeId, name: &str) -> bool {
self.0.answers_to(n, name)
}
fn has_trait(&self, n: NodeId, name: &str) -> bool {
self.0.has_trait(n, name)
}
fn parent(&self, n: NodeId) -> Option<NodeId> {
self.0.parent(n)
}
fn traits(&self, n: NodeId) -> Vec<String> {
self.0.traits(n)
}
fn children_named(&self, n: NodeId, name: &str) -> Vec<NodeId> {
self.0.children_named(n, name)
}
fn child_host(&self, n: NodeId) -> NodeId {
self.0.child_host(n)
}
fn non_tree_nodes(&self) -> Vec<NodeId> {
self.0.non_tree_nodes()
}
fn properties(&self, n: NodeId) -> Vec<(String, Value)> {
self.0.properties(n)
}
fn property(&self, n: NodeId, name: &str) -> Option<Value> {
self.0.property(n, name)
}
fn default_value(&self, n: NodeId) -> Option<Value> {
self.0.default_value(n)
}
fn metadata(&self, n: NodeId, key: &str) -> Option<Value> {
self.0.metadata(n, key)
}
fn links(&self, n: NodeId) -> Vec<(String, NodeId)> {
self.0.links(n)
}
fn backlinks(&self, n: NodeId) -> Vec<(String, NodeId)> {
self.0.backlinks(n)
}
fn resolve(&self, n: NodeId, p: &str, h: Option<&str>) -> Option<NodeId> {
self.0.resolve(n, p, h)
}
fn external_ref(&self, n: NodeId, p: &str, h: Option<&str>) -> Option<String> {
self.0.external_ref(n, p, h)
}
fn resolve_fragment(&self, n: NodeId, f: &str) -> Option<NodeId> {
self.0.resolve_fragment(n, f)
}
fn ref_property(&self, n: NodeId) -> Option<String> {
self.0.ref_property(n)
}
fn ref_label(&self, n: NodeId, p: &str) -> Option<String> {
self.0.ref_label(n, p)
}
fn link_property(&self, s: NodeId, l: &str, t: NodeId, name: &str) -> Option<Value> {
self.0.link_property(s, l, t, name)
}
fn document_by_ref(&self, id: &str) -> Option<NodeId> {
self.0.document_by_ref(id)
}
fn reverse_resolve(&self, n: NodeId, p: Option<&str>, h: Option<&str>) -> Option<Vec<NodeId>> {
self.0.reverse_resolve(n, p, h)
}
fn descendants_named(&self, n: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
self.0.descendants_named(n, name)
}
fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
self.0.prefetch_links(nodes, dir)
}
fn quantifier_bound(&self) -> usize {
self.0.quantifier_bound()
}
fn allow_shell(&self) -> bool {
self.0.allow_shell()
}
fn invocation_instant(&self) -> Option<(i64, u32)> {
self.0.invocation_instant()
}
fn provenance(&self, n: NodeId) -> quarb::Provenance {
self.0.provenance(n)
}
fn provenance_list(&self, n: NodeId) -> quarb::ProvenanceList {
self.0.provenance_list(n)
}
fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
self.0.unit_scale(expr)
}
}