1mod parse;
18
19pub use parse::{
20 AliasDecl, AliasKind, AliasRule, EdgeDecl, Model, Mount, NodeDecl, RefDecl, RelDecl, UnitsDecl,
21 parse_model, parse_model_file, resolve_mount_target,
22};
23
24use quarb::{AstAdapter, NodeId, QueryResult, Value};
25use std::cell::{Cell, OnceCell, RefCell};
26use std::collections::{BTreeMap, HashMap};
27
28const MODEL_TAG: u64 = 1 << 63;
33const CIDX_SHIFT: u64 = 44;
34const VAL_MASK: u64 = (1 << CIDX_SHIFT) - 1;
35
36#[derive(Clone)]
40enum Member {
41 Value(Value),
44 Node(NodeId),
49}
50
51struct Container {
56 name: String,
57 members: Vec<Member>,
62 roles: Vec<(String, String)>,
66 by_str: HashMap<String, usize>,
69 by_role: HashMap<String, HashMap<String, usize>>,
71}
72
73impl Container {
74 fn member(&self, v: usize) -> Option<Member> {
76 if v == 0 {
77 return None;
78 }
79 self.members.get(v - 1).cloned()
80 }
81
82 fn role_of(&self, v: usize) -> &str {
84 &self.roles[v - 1].0
85 }
86
87 fn trait_of(&self, v: usize) -> &str {
89 &self.roles[v - 1].1
90 }
91
92 fn ordinal_in_role(&self, v: usize) -> usize {
95 let role = &self.roles[v - 1].0;
96 self.roles[..v - 1]
97 .iter()
98 .filter(|(r, _)| r == role)
99 .count()
100 + 1
101 }
102
103 fn of_role<'a>(&'a self, role: &'a str) -> impl Iterator<Item = usize> + 'a {
105 self.roles
106 .iter()
107 .enumerate()
108 .filter(move |(_, (r, _))| r == role)
109 .map(|(i, _)| i)
110 }
111}
112
113struct Fabric {
116 alias: HashMap<NodeId, (NodeId, String)>,
121 resolve: HashMap<(NodeId, String), NodeId>,
123 sources: HashMap<(NodeId, String), Vec<NodeId>>,
127 ref_back: HashMap<NodeId, Vec<(String, NodeId)>>,
130 ref_fwd: HashMap<NodeId, Vec<(String, NodeId)>>,
132 rel_fwd: HashMap<NodeId, Vec<(String, NodeId)>>,
136 rel_back: HashMap<NodeId, Vec<(String, NodeId)>>,
137 edges: HashMap<NodeId, Vec<(String, NodeId)>>,
141}
142
143pub struct ModelAdapter<A: AstAdapter> {
145 base: A,
146 model: Model,
147 containers: OnceCell<Vec<Container>>,
148 fabric: OnceCell<Fabric>,
149 bodies: OnceCell<Vec<quarb::StepBody>>,
152 decided: RefCell<HashMap<(usize, NodeId), bool>>,
156 visible: Cell<usize>,
161 indexes: OnceCell<Vec<ValueIndex>>,
166 trees: OnceCell<Vec<(String, NodeId)>>,
168 ref_scopes: OnceCell<Vec<std::collections::HashSet<NodeId>>>,
171 referrers: RefCell<HashMap<(usize, Option<String>), HashMap<NodeId, Vec<usize>>>>,
172 units: Result<BTreeMap<String, kaiv::faiv::UnitDef>, String>,
176}
177
178struct ValueIndex {
180 scope: String,
181 property: String,
182 nodes: Vec<NodeId>,
183 by_value: HashMap<String, Vec<NodeId>>,
184}
185
186fn index_key(v: &Value) -> Option<String> {
188 v.equality_key()
189}
190
191impl<A: AstAdapter> ModelAdapter<A> {
192 fn value_indexes(&self) -> &Vec<ValueIndex> {
193 self.indexes.get_or_init(|| {
194 let defs = self.seeded_defs();
195 self.model
196 .indexes
197 .iter()
198 .map(|d| {
199 let scope = quarb::parse_scope_path(&d.scope)
200 .map(|sp| sp.text())
201 .unwrap_or_else(|_| d.scope.clone());
202 let nodes = self.view_nodes(&d.scope, &defs);
203 let mut by_value: HashMap<String, Vec<NodeId>> = HashMap::new();
204 for &n in &nodes {
205 if let Some(v) = self.property(n, &d.property) {
206 let vals: Vec<&Value> = match &v {
209 Value::List(items) => items.iter().collect(),
210 other => vec![other],
211 };
212 for item in vals {
213 if let Some(k) = index_key(item) {
214 by_value.entry(k).or_default().push(n);
215 }
216 }
217 }
218 }
219 ValueIndex {
220 scope,
221 property: d.property.clone(),
222 nodes,
223 by_value,
224 }
225 })
226 .collect()
227 })
228 }
229
230 fn ref_scopes(&self) -> &Vec<std::collections::HashSet<NodeId>> {
231 self.ref_scopes.get_or_init(|| {
232 let defs = self.seeded_defs();
233 self.model
234 .refs
235 .iter()
236 .map(|decl| {
237 let mut scoped = self.scope_nodes(&decl.scope, &defs);
238 if scoped.is_empty() {
239 scoped = self
240 .view_nodes(&decl.scope, &defs)
241 .into_iter()
242 .filter_map(|n| self.aliased(n))
243 .collect();
244 }
245 scoped.into_iter().collect()
246 })
247 .collect()
248 })
249 }
250
251 fn declared_fields(&self, source: NodeId, target: NodeId) -> Vec<String> {
256 let src = self.aliased(source).unwrap_or(source);
257 let mut homes: Vec<String> = Vec::new();
258 match self.decode(target) {
259 Some((c, _)) => homes.push(self.containers()[c].name.clone()),
260 None => {
261 let mut at = self.base.parent(target);
262 while let Some(p) = at {
263 if let Some(name) = self.base.name(p) {
264 homes.push(name);
265 }
266 at = self.base.parent(p);
267 }
268 }
269 }
270 let mut out: Vec<String> = Vec::new();
271 for (decl, scope) in self.model.refs.iter().zip(self.ref_scopes()) {
272 if scope.contains(&src) && homes.contains(&decl.container) && !out.contains(&decl.field)
273 {
274 out.push(decl.field.clone());
275 }
276 }
277 out
278 }
279
280 fn value_index(&self, scope: &str, property: &str) -> Option<(usize, &ValueIndex)> {
281 self.value_indexes()
282 .iter()
283 .enumerate()
284 .find(|(_, ix)| ix.scope == scope && ix.property == property)
285 }
286
287 pub fn new(base: A, model: Model) -> Self {
288 let n = model.traits.len();
289 let model_units = model.units.clone();
290 ModelAdapter {
291 base,
292 model,
293 containers: OnceCell::new(),
294 fabric: OnceCell::new(),
295 bodies: OnceCell::new(),
296 decided: RefCell::new(HashMap::new()),
297 visible: Cell::new(n),
298 indexes: OnceCell::new(),
299 ref_scopes: OnceCell::new(),
300 trees: OnceCell::new(),
301 referrers: RefCell::new(HashMap::new()),
302 units: Self::load_units(&model_units),
303 }
304 }
305
306 fn load_units(decls: &[UnitsDecl]) -> Result<BTreeMap<String, kaiv::faiv::UnitDef>, String> {
310 let mut table: BTreeMap<String, kaiv::faiv::UnitDef> = BTreeMap::new();
311 for decl in decls {
312 let bytes =
313 std::fs::read(&decl.path).map_err(|e| format!("units {}: {e}", decl.library))?;
314 let lib = kaiv::faiv::parse_faiv(&bytes)
315 .map_err(|e| format!("units {}: {e}", decl.library))?;
316 for (name, def) in lib.units {
317 if kaiv::unit::scale(&name).is_some() {
318 return Err(format!(
319 "units {}: '{name}' is a built-in unit already",
320 decl.library
321 ));
322 }
323 if table.contains_key(&name) {
324 return Err(format!(
325 "units {}: '{name}' is already defined by an earlier library",
326 decl.library
327 ));
328 }
329 table.insert(name, def);
330 }
331 }
332 Ok(table)
333 }
334
335 pub fn check(&self) -> Result<(), String> {
338 if let Err(e) = &self.units {
339 return Err(e.clone());
340 }
341 self.check_declared_traits()?;
342 self.check_ref_targets()?;
343 self.check_trees()
344 }
345
346 pub fn check_trees(&self) -> Result<(), String> {
348 let defs = self.seeded_defs();
349 for decl in &self.model.trees {
350 if self.view_nodes(&decl.path, &defs).is_empty() {
351 return Err(format!(
352 "tree '{}': the path {} names no node",
353 decl.identity, decl.path
354 ));
355 }
356 }
357 Ok(())
358 }
359
360 pub fn check_ref_targets(&self) -> Result<(), String> {
367 let defs = self.seeded_defs();
368 for decl in &self.model.refs {
369 if self.model.nodes.iter().any(|n| n.name == decl.container) {
370 continue;
371 }
372 let found = [
373 format!("/{}", decl.container),
374 format!("/\"{}\"", decl.container),
375 ]
376 .iter()
377 .any(|q| !self.scope_nodes(q, &defs).is_empty());
378 if !found {
379 return Err(format!(
380 "ref target '{}' names nothing: no 'node /{}/…' statement \
381 declares that container and the source has no \
382 root-level '{}' (ref {}::{} --> {})",
383 decl.container,
384 decl.container,
385 decl.container,
386 decl.scope,
387 decl.field,
388 decl.path
389 ));
390 }
391 }
392 Ok(())
393 }
394
395 pub fn check_declared_traits(&self) -> Result<(), String> {
401 for decl in &self.model.traits {
402 if let Some(n) = self.model.nodes.iter().find(|n| n.trait_name == decl.name) {
403 return Err(format!(
404 "trait <{}> is already the trait of the derived container /{} — \
405 a declared trait cannot redefine one the arbor has",
406 decl.name, n.name
407 ));
408 }
409 let q = format!("*//*<{}> @| count", decl.name);
410 if let Ok(QueryResult::Values(vs)) = quarb::run(&q, &self.base)
411 && matches!(vs.first(), Some(Value::Int(n)) if *n > 0)
412 {
413 return Err(format!(
414 "trait <{}> is the adapter's own — a declared trait cannot \
415 redefine one the arbor has",
416 decl.name
417 ));
418 }
419 }
420 Ok(())
421 }
422
423 fn declared_traits_of(&self, node: NodeId) -> Vec<String> {
426 let mut out = Vec::new();
427 for i in 0..self.visible.get().min(self.model.traits.len()) {
428 if self.declared_at(i, node) {
429 out.push(self.model.traits[i].name.clone());
430 }
431 }
432 out
433 }
434
435 fn declared_at(&self, i: usize, node: NodeId) -> bool {
439 if let Some(&d) = self.decided.borrow().get(&(i, node)) {
440 return d;
441 }
442 let bodies = self.bodies.get_or_init(|| {
443 self.model
444 .traits
445 .iter()
446 .map(|t| {
447 quarb::parse_step_body(&t.step).expect("validated when the model was parsed")
448 })
449 .collect()
450 });
451 let was = self.visible.get();
452 self.visible.set(i);
453 let d = quarb::node_matches_step_body(self, node, &bodies[i]);
454 self.visible.set(was);
455 self.decided.borrow_mut().insert((i, node), d);
456 d
457 }
458
459 pub fn base(&self) -> &A {
460 &self.base
461 }
462
463 pub fn locator(&self, node: NodeId, base_locator: impl Fn(NodeId) -> String) -> String {
466 match self.decode(node) {
467 None => base_locator(node),
468 Some((c, 0)) => format!("/{}", self.containers()[c].name),
469 Some((c, v)) => {
470 let cont = &self.containers()[c];
473 format!(
474 "/{}/{}[{}]",
475 cont.name,
476 cont.role_of(v),
477 cont.ordinal_in_role(v)
478 )
479 }
480 }
481 }
482
483 fn seeded_defs(&self) -> quarb::Defs {
484 quarb::parse_defs(&self.model.defs_text).unwrap_or_default()
485 }
486
487 fn containers(&self) -> &[Container] {
492 self.containers.get_or_init(|| {
493 let defs = self.seeded_defs();
494 let mut built: Vec<Container> = Vec::new();
495 for decl in &self.model.nodes {
496 let members = self.members(&decl.query, &defs, &built);
497 let idx = match built.iter().position(|c| c.name == decl.name) {
500 Some(i) => i,
501 None => {
502 built.push(Container {
503 name: decl.name.clone(),
504 members: Vec::new(),
505 roles: Vec::new(),
506 by_str: HashMap::new(),
507 by_role: HashMap::new(),
508 });
509 built.len() - 1
510 }
511 };
512 let keyed: Vec<(Member, Option<String>)> = members
517 .into_iter()
518 .map(|m| {
519 let key = match &m {
520 Member::Value(v) => Some(v.to_string()),
521 Member::Node(n) => self.base_key(*n),
522 };
523 (m, key)
524 })
525 .collect();
526 let cont = &mut built[idx];
527 let held_nodes: std::collections::HashSet<NodeId> = cont
532 .members
533 .iter()
534 .zip(&cont.roles)
535 .filter(|(_, (r, _))| *r == decl.role)
536 .filter_map(|(m, _)| match m {
537 Member::Node(n) => Some(*n),
538 Member::Value(_) => None,
539 })
540 .collect();
541 let by_role = cont.by_role.entry(decl.role.clone()).or_default();
542 for (m, key) in keyed {
543 let held = match &m {
544 Member::Node(n) => held_nodes.contains(n),
545 Member::Value(_) => key.as_deref().is_some_and(|k| by_role.contains_key(k)),
546 };
547 if held {
548 continue;
549 }
550 let i = cont.members.len();
551 cont.members.push(m);
552 cont.roles
553 .push((decl.role.clone(), decl.trait_name.clone()));
554 if let Some(k) = key {
555 by_role.entry(k.clone()).or_insert(i);
556 cont.by_str.entry(k).or_insert(i);
557 }
558 }
559 }
560 built
561 })
562 }
563
564 fn members(&self, query: &str, defs: &quarb::Defs, prior: &[Container]) -> Vec<Member> {
571 let result = if prior.is_empty() {
575 quarb::run_with_defs(query, defs, &self.base)
576 } else {
577 let scratch = PriorView {
578 base: &self.base,
579 prior,
580 };
581 quarb::run_with_defs(query, defs, &scratch)
582 };
583 match result {
584 Ok(QueryResult::Values(vs)) => {
585 let mut seen = std::collections::HashSet::new();
586 vs.into_iter()
587 .filter(|v| seen.insert(v.to_string()))
588 .map(Member::Value)
589 .collect()
590 }
591 Ok(QueryResult::Nodes(ns)) => {
592 let mut seen = std::collections::HashSet::new();
593 ns.into_iter()
594 .filter(|n| seen.insert(*n))
595 .map(Member::Node)
596 .collect()
597 }
598 Err(_) => Vec::new(),
599 }
600 }
601
602 fn base_key(&self, node: NodeId) -> Option<String> {
605 self.base
606 .default_value(node)
607 .map(|v| v.to_string())
608 .or_else(|| self.base.name(node))
609 }
610
611 fn aliased(&self, node: NodeId) -> Option<NodeId> {
613 match self.decode(node) {
614 Some((c, v)) if v > 0 => match self.containers()[c].member(v)? {
615 Member::Node(n) => Some(n),
616 Member::Value(_) => None,
617 },
618 _ => None,
619 }
620 }
621
622 fn role_at(&self, node: NodeId) -> Option<String> {
625 match self.decode(node) {
626 Some((c, v)) if v > 0 => Some(self.containers()[c].role_of(v).to_string()),
627 _ => None,
628 }
629 }
630
631 fn elevated(&self, node: NodeId) -> Option<Value> {
633 match self.decode(node) {
634 Some((c, v)) if v > 0 => match self.containers()[c].member(v)? {
635 Member::Value(val) => Some(val),
636 Member::Node(_) => None,
637 },
638 _ => None,
639 }
640 }
641
642 fn container_node(c: usize) -> NodeId {
643 NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT)
644 }
645
646 fn value_node(c: usize, v: usize) -> NodeId {
647 NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (v as u64 + 1))
648 }
649
650 fn decode(&self, node: NodeId) -> Option<(usize, usize)> {
654 if node.0 & MODEL_TAG == 0 {
655 return None;
656 }
657 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
658 let v = (node.0 & VAL_MASK) as usize;
659 (c < self.containers().len()).then_some((c, v))
660 }
661
662 fn container_by_name(&self, name: &str) -> Option<usize> {
663 self.containers().iter().position(|c| c.name == name)
664 }
665
666 fn fabric(&self) -> &Fabric {
668 self.fabric.get_or_init(|| {
669 let defs = self.seeded_defs();
670 let mut f = Fabric {
671 alias: HashMap::new(),
672 resolve: HashMap::new(),
673 sources: HashMap::new(),
674 ref_back: HashMap::new(),
675 ref_fwd: HashMap::new(),
676 rel_fwd: HashMap::new(),
677 rel_back: HashMap::new(),
678 edges: HashMap::new(),
679 };
680 for (c, cont) in self.containers().iter().enumerate() {
681 for (i, m) in cont.members.iter().enumerate() {
682 if let Member::Node(n) = m {
683 f.alias
684 .entry(*n)
685 .or_insert((Self::value_node(c, i), cont.roles[i].0.clone()));
686 }
687 }
688 }
689 let mut reached: HashMap<(NodeId, String), Vec<(String, NodeId)>> = HashMap::new();
693 for decl in &self.model.refs {
694 let mut index: HashMap<String, NodeId> = HashMap::new();
703 let derived = self.container_by_name(&decl.container);
704 match derived {
705 Some(container) => {
706 let cont = &self.containers()[container];
707 let slots: Vec<usize> = match decl.role.as_deref() {
708 Some(role) => cont.of_role(role).map(|i| i + 1).collect(),
709 None => (1..=cont.members.len()).collect(),
710 };
711 for v in slots {
712 let n = Self::value_node(container, v - 1);
713 let key = match decl.key_field.as_deref() {
714 Some(f) => self.property(n, f).map(|k| k.to_string()),
715 None => match cont.member(v) {
716 Some(Member::Value(val)) => Some(val.to_string()),
717 Some(Member::Node(b)) => self.base_key(b),
718 None => None,
719 },
720 };
721 if let Some(k) = key {
722 index.entry(k).or_insert(n);
723 }
724 }
725 }
726 None => {
727 let view = PriorView {
728 base: &self.base,
729 prior: self.containers(),
730 };
731 let path = if decl.path.starts_with('/') {
736 decl.path.clone()
737 } else {
738 format!("/{}/*", decl.path)
739 };
740 for n in self.view_nodes(&path, &defs) {
741 let key = match decl.key_field.as_deref() {
742 Some(f) => view.property(n, f).map(|k| k.to_string()),
743 None => view
744 .default_value(n)
745 .map(|v| v.to_string())
746 .or_else(|| view.name(n)),
747 };
748 if let Some(k) = key {
749 index.entry(k).or_insert(n);
750 }
751 }
752 }
753 }
754 let path_label = scope_role(&decl.path);
758 let mut scoped = self.scope_nodes(&decl.scope, &defs);
762 if scoped.is_empty() {
763 scoped = self
764 .view_nodes(&decl.scope, &defs)
765 .into_iter()
766 .filter_map(|n| self.aliased(n))
767 .collect();
768 }
769 for node in scoped {
770 let Some(value) = self.base.property(node, &decl.field) else {
771 continue;
772 };
773 if matches!(value, Value::Null) {
774 continue;
775 }
776 let elements: Vec<Value> = match value {
781 l @ Value::List(_) => l.into_items().unwrap_or_default(),
782 other => vec![other],
783 };
784 let mut first = true;
785 for value in elements {
786 let Some(target) = index.get(&value.to_string()).copied() else {
787 continue;
788 };
789 let fwd = self.role_at(target).unwrap_or_else(|| path_label.clone());
790 let (back_node, back) = match f.alias.get(&node) {
794 Some((alias, role)) => (*alias, role.clone()),
795 None => (node, scope_role(&decl.scope)),
796 };
797 if first {
798 f.resolve.insert((node, decl.field.clone()), target);
799 first = false;
800 }
801 reached
802 .entry((node, decl.field.clone()))
803 .or_default()
804 .push((fwd.clone(), target));
805 let from = f.sources.entry((target, decl.field.clone())).or_default();
806 if !from.contains(&back_node) {
807 from.push(back_node);
808 }
809 f.ref_fwd.entry(node).or_default().push((fwd, target));
810 f.ref_back
811 .entry(target)
812 .or_default()
813 .push((back, back_node));
814 }
815 }
816 }
817 for decl in &self.model.rels {
824 let fwd = scope_role(&decl.target);
825 let back = scope_role(&decl.source);
826 for source in self.view_nodes(&decl.source, &defs) {
827 let cond = self.bind_driver(&decl.cond, source);
828 let query = format!("{}{}", decl.target, cond);
829 for target in self.view_nodes(&query, &defs) {
830 if target == source {
831 continue;
832 }
833 f.rel_fwd
834 .entry(source)
835 .or_default()
836 .push((fwd.clone(), target));
837 f.rel_back
838 .entry(target)
839 .or_default()
840 .push((back.clone(), source));
841 }
842 }
843 }
844 for decl in &self.model.edges {
852 let mut seen = std::collections::HashSet::new();
853 for node in self.scope_nodes(&decl.scope, &defs) {
854 let (Some(ends_a), Some(ends_b)) = (
855 reached.get(&(node, decl.field_a.clone())),
856 reached.get(&(node, decl.field_b.clone())),
857 ) else {
858 continue;
859 };
860 for (ra, na) in ends_a {
861 for (rb, nb) in ends_b {
862 if na != nb && seen.insert((*na, *nb)) {
863 f.edges.entry(*na).or_default().push((rb.clone(), *nb));
864 f.edges.entry(*nb).or_default().push((ra.clone(), *na));
865 }
866 }
867 }
868 }
869 }
870 f
871 })
872 }
873
874 fn scope_nodes(&self, scope: &str, defs: &quarb::Defs) -> Vec<NodeId> {
876 match quarb::run_with_defs(scope, defs, &self.base) {
877 Ok(QueryResult::Nodes(ns)) => ns,
878 _ => Vec::new(),
879 }
880 }
881
882 fn view_nodes(&self, path: &str, defs: &quarb::Defs) -> Vec<NodeId> {
887 let view = PriorView {
888 base: &self.base,
889 prior: self.containers(),
890 };
891 match quarb::run_with_defs(path, defs, &view) {
892 Ok(QueryResult::Nodes(ns)) => ns,
893 _ => Vec::new(),
894 }
895 }
896
897 fn bind_driver(&self, cond: &str, source: NodeId) -> String {
902 let view = PriorView {
903 base: &self.base,
904 prior: self.containers(),
905 };
906 let b: Vec<char> = cond.chars().collect();
907 let mut out = String::new();
908 let mut i = 0;
909 while i < b.len() {
910 match b[i] {
911 q @ ('\'' | '"') => {
912 out.push(q);
913 i += 1;
914 while i < b.len() && b[i] != q {
915 if b[i] == '\\' && i + 1 < b.len() {
916 out.push(b[i]);
917 i += 1;
918 }
919 out.push(b[i]);
920 i += 1;
921 }
922 if i < b.len() {
923 out.push(b[i]);
924 i += 1;
925 }
926 }
927 c @ ('$' | '_')
930 if (c == '$' && b.get(i + 1) == Some(&'$'))
931 || (c == '_'
932 && !(i > 0
933 && (b[i - 1].is_alphanumeric()
934 || b[i - 1] == '_'
935 || b[i - 1] == '-'))
936 && !b.get(i + 1).is_some_and(|c| {
937 c.is_alphanumeric() || *c == '_' || *c == '-'
938 })) =>
939 {
940 if c == '$' {
941 i += 2;
942 if b.get(i) == Some(&'_')
943 && !b
944 .get(i + 1)
945 .is_some_and(|c| c.is_alphanumeric() || *c == '_' || *c == '-')
946 {
947 i += 1;
948 }
949 } else {
950 i += 1;
951 }
952 let value = if b.get(i) == Some(&':') && b.get(i + 1) == Some(&':') {
953 i += 2;
954 let start = i;
955 while i < b.len() && (b[i].is_alphanumeric() || b[i] == '_' || b[i] == '-')
956 {
957 i += 1;
958 }
959 let field: String = b[start..i].iter().collect();
960 view.property(source, &field)
961 } else {
962 view.default_value(source)
963 };
964 out.push_str(&literal(value.unwrap_or(Value::Null)));
965 }
966 c => {
967 out.push(c);
968 i += 1;
969 }
970 }
971 }
972 out
973 }
974}
975
976fn literal(v: Value) -> String {
979 match v {
980 Value::Int(n) => n.to_string(),
981 Value::Float(f) => f.to_string(),
982 Value::Bool(b) => b.to_string(),
983 Value::Null => "''".to_string(),
984 other => format!(
985 "'{}'",
986 other.to_string().replace('\\', "\\\\").replace('\'', "\\'")
987 ),
988 }
989}
990
991struct PriorView<'a, A: AstAdapter> {
995 base: &'a A,
996 prior: &'a [Container],
997}
998
999impl<A: AstAdapter> AstAdapter for PriorView<'_, A> {
1000 fn root(&self) -> NodeId {
1001 self.base.root()
1002 }
1003 fn children(&self, node: NodeId) -> Vec<NodeId> {
1004 if node.0 & MODEL_TAG != 0 {
1005 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
1006 let v = (node.0 & VAL_MASK) as usize;
1007 if v == 0 && c < self.prior.len() {
1008 return (0..self.prior[c].members.len())
1009 .map(|i| NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (i as u64 + 1)))
1010 .collect();
1011 }
1012 return Vec::new();
1013 }
1014 let mut kids = self.base.children(node);
1015 if node == self.base.root() {
1016 for c in 0..self.prior.len() {
1017 kids.push(NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT));
1018 }
1019 }
1020 kids
1021 }
1022 fn name(&self, node: NodeId) -> Option<String> {
1023 if node.0 & MODEL_TAG != 0 {
1024 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
1025 let v = (node.0 & VAL_MASK) as usize;
1026 let cont = self.prior.get(c)?;
1027 return Some(if v == 0 {
1030 cont.name.clone()
1031 } else {
1032 cont.role_of(v).to_string()
1033 });
1034 }
1035 self.base.name(node)
1036 }
1037 fn child_host(&self, node: NodeId) -> NodeId {
1038 if node.0 & MODEL_TAG != 0 {
1039 node
1040 } else {
1041 self.base.child_host(node)
1042 }
1043 }
1044 fn non_tree_nodes(&self) -> Vec<NodeId> {
1045 self.base.non_tree_nodes()
1046 }
1047 fn walks_for_references(&self) -> bool {
1048 self.base.walks_for_references()
1049 }
1050 fn properties(&self, node: NodeId) -> Vec<(String, Value)> {
1051 if node.0 & MODEL_TAG != 0 {
1052 Vec::new()
1053 } else {
1054 self.base.properties(node)
1055 }
1056 }
1057 fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
1058 if node == self.base.root() {
1059 let mut out = self.base.children_named(node, name);
1060 if let Some(c) = self.prior.iter().position(|k| k.name == name) {
1061 out.push(NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT));
1062 }
1063 return out;
1064 }
1065 if node.0 & MODEL_TAG != 0 {
1066 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
1067 let v = (node.0 & VAL_MASK) as usize;
1068 let Some(cont) = self.prior.get(c) else {
1069 return Vec::new();
1070 };
1071 if v == 0 {
1072 return cont
1073 .of_role(name)
1074 .map(|i| NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (i as u64 + 1)))
1075 .collect();
1076 }
1077 return Vec::new();
1078 }
1079 self.base.children_named(node, name)
1080 }
1081 fn traits(&self, node: NodeId) -> Vec<String> {
1082 if node.0 & MODEL_TAG != 0 {
1083 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
1084 let v = (node.0 & VAL_MASK) as usize;
1085 return match self.prior.get(c) {
1086 Some(cont) if v > 0 => vec![cont.trait_of(v).to_string()],
1087 _ => Vec::new(),
1088 };
1089 }
1090 self.base.traits(node)
1091 }
1092 fn parent(&self, node: NodeId) -> Option<NodeId> {
1093 if node.0 & MODEL_TAG != 0 {
1094 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
1095 let v = (node.0 & VAL_MASK) as usize;
1096 return Some(if v == 0 {
1097 self.base.root()
1098 } else {
1099 NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT)
1100 });
1101 }
1102 self.base.parent(node)
1103 }
1104 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
1105 if node.0 & MODEL_TAG != 0 {
1106 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
1107 let v = (node.0 & VAL_MASK) as usize;
1108 return match self.prior.get(c)?.member(v)? {
1109 Member::Value(_) => None,
1112 Member::Node(n) => self.base.property(n, name),
1113 };
1114 }
1115 self.base.property(node, name)
1116 }
1117 fn default_value(&self, node: NodeId) -> Option<Value> {
1118 if node.0 & MODEL_TAG != 0 {
1119 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
1120 let v = (node.0 & VAL_MASK) as usize;
1121 return match self.prior.get(c)?.member(v)? {
1122 Member::Value(val) => Some(val),
1123 Member::Node(n) => self.base.default_value(n),
1124 };
1125 }
1126 self.base.default_value(node)
1127 }
1128 fn document_by_ref(&self, id: &str) -> Option<NodeId> {
1132 self.base.document_by_ref(id)
1133 }
1134 fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
1135 if node.0 & MODEL_TAG != 0 || node == self.base.root() {
1136 return None;
1137 }
1138 self.base.descendants_named(node, name)
1139 }
1140 fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
1141 let base: Vec<NodeId> = nodes
1142 .iter()
1143 .copied()
1144 .filter(|n| n.0 & MODEL_TAG == 0)
1145 .collect();
1146 if !base.is_empty() {
1147 self.base.prefetch_links(&base, dir);
1148 }
1149 }
1150 fn provenance(&self, node: NodeId) -> quarb::Provenance {
1151 if node.0 & MODEL_TAG != 0 {
1152 return quarb::Provenance::default();
1153 }
1154 self.base.provenance(node)
1155 }
1156 fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
1157 if node.0 & MODEL_TAG != 0 {
1158 return quarb::ProvenanceList::default();
1159 }
1160 self.base.provenance_list(node)
1161 }
1162}
1163
1164impl<A: AstAdapter> AstAdapter for ModelAdapter<A> {
1165 fn root(&self) -> NodeId {
1166 self.base.root()
1167 }
1168
1169 fn children(&self, node: NodeId) -> Vec<NodeId> {
1170 match self.decode(node) {
1171 Some((c, 0)) => (0..self.containers()[c].members.len())
1173 .map(|v| Self::value_node(c, v))
1174 .collect(),
1175 Some(_) => match self.aliased(node) {
1178 Some(base) => self.base.children(base),
1179 None => Vec::new(),
1180 },
1181 None => {
1184 let mut kids = self.base.children(node);
1185 if node == self.base.root() {
1186 for c in 0..self.containers().len() {
1187 kids.push(Self::container_node(c));
1188 }
1189 }
1190 kids
1191 }
1192 }
1193 }
1194
1195 fn child_host(&self, node: NodeId) -> NodeId {
1198 match self.decode(node) {
1199 Some(_) => node,
1200 None => self.base.child_host(node),
1201 }
1202 }
1203 fn non_tree_nodes(&self) -> Vec<NodeId> {
1204 self.base.non_tree_nodes()
1205 }
1206 fn walks_for_references(&self) -> bool {
1207 self.base.walks_for_references()
1208 }
1209 fn can_refer_to(&self, source: NodeId, target: NodeId) -> Option<bool> {
1214 if !self.declared_fields(source, target).is_empty() {
1215 return Some(true);
1216 }
1217 if self.decode(source).is_none() && self.decode(target).is_none() {
1218 return self.base.can_refer_to(source, target);
1219 }
1220 None
1221 }
1222 fn reference_properties(&self, source: NodeId, target: NodeId) -> Vec<String> {
1225 let fields = self.declared_fields(source, target);
1226 if !fields.is_empty() {
1227 return fields;
1228 }
1229 if self.decode(source).is_none() && self.decode(target).is_none() {
1230 return self.base.reference_properties(source, target);
1231 }
1232 Vec::new()
1233 }
1234 fn resolves_references(&self) -> bool {
1235 !self.model.refs.is_empty() || self.base.resolves_references()
1236 }
1237 fn reverse_label(&self, target: NodeId) -> Option<String> {
1240 if self.decode(target).is_some() {
1241 return None;
1242 }
1243 self.base.reverse_label(target)
1244 }
1245 fn tree_identities(&self) -> Vec<(String, NodeId)> {
1249 let declared = self.trees.get_or_init(|| {
1250 let defs = self.seeded_defs();
1251 self.model
1252 .trees
1253 .iter()
1254 .filter_map(|d| {
1255 self.view_nodes(&d.path, &defs)
1256 .first()
1257 .map(|&n| (d.identity.clone(), n))
1258 })
1259 .collect()
1260 });
1261 let mut out = declared.clone();
1262 out.extend(self.base.tree_identities());
1263 out
1264 }
1265 fn resolve_needs_label(&self, node: NodeId, property: &str) -> bool {
1268 self.decode(node).is_none()
1269 && !self.model.refs.iter().any(|r| r.field == property)
1270 && self.base.resolve_needs_label(node, property)
1271 }
1272 fn property_names(&self, node: NodeId) -> Vec<String> {
1273 if self.decode(node).is_none() {
1274 return self.base.property_names(node);
1275 }
1276 let mut ps: Vec<String> = self.properties(node).into_iter().map(|(k, _)| k).collect();
1277 for c in self.children(node) {
1278 if let Some(name) = self.name(c)
1279 && !ps.contains(&name)
1280 {
1281 ps.push(name);
1282 }
1283 }
1284 ps
1285 }
1286 fn index_lookup(&self, scope: &str, property: &str, value: &Value) -> Option<Vec<NodeId>> {
1287 let (_, ix) = self.value_index(scope, property)?;
1288 let key = index_key(value)?;
1289 Some(ix.by_value.get(&key).cloned().unwrap_or_default())
1290 }
1291 fn index_referrers(
1292 &self,
1293 scope: &str,
1294 property: &str,
1295 label: Option<&str>,
1296 target: NodeId,
1297 ) -> Option<Vec<NodeId>> {
1298 let Some((i, ix)) = self.value_index(scope, property) else {
1299 if self.decode(target).is_none() && !self.model.refs.iter().any(|r| r.field == property)
1303 {
1304 return self.base.index_referrers(scope, property, label, target);
1305 }
1306 return None;
1307 };
1308 let mut labels = vec![label.map(str::to_string)];
1313 if label.is_none()
1314 && let Some(implied) = self.reverse_label(target)
1315 {
1316 labels.push(Some(implied));
1317 }
1318 let mut at: Vec<usize> = Vec::new();
1319 for l in labels {
1320 let key = (i, l);
1321 if !self.referrers.borrow().contains_key(&key) {
1322 let mut map: HashMap<NodeId, Vec<usize>> = HashMap::new();
1325 for (pos, &n) in ix.nodes.iter().enumerate() {
1326 if let Some(t) = self.resolve(n, property, key.1.as_deref()) {
1327 map.entry(t).or_default().push(pos);
1328 }
1329 }
1330 self.referrers.borrow_mut().insert(key.clone(), map);
1331 }
1332 if let Some(hits) = self
1333 .referrers
1334 .borrow()
1335 .get(&key)
1336 .and_then(|m| m.get(&target))
1337 {
1338 at.extend(hits);
1339 }
1340 }
1341 at.sort_unstable();
1342 at.dedup();
1343 Some(at.into_iter().map(|pos| ix.nodes[pos]).collect())
1344 }
1345 fn properties(&self, node: NodeId) -> Vec<(String, Value)> {
1346 if node.0 & MODEL_TAG != 0 {
1347 Vec::new()
1348 } else {
1349 self.base.properties(node)
1350 }
1351 }
1352
1353 fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
1354 if node == self.base.root() {
1356 let mut out = self.base.children_named(node, name);
1357 if let Some(c) = self.container_by_name(name) {
1358 out.push(Self::container_node(c));
1359 }
1360 return out;
1361 }
1362 match self.decode(node) {
1363 Some((c, 0)) => self.containers()[c]
1366 .of_role(name)
1367 .map(|i| Self::value_node(c, i))
1368 .collect(),
1369 Some(_) => match self.aliased(node) {
1370 Some(base) => self.base.children_named(base, name),
1371 None => Vec::new(),
1372 },
1373 None => self.base.children_named(node, name),
1374 }
1375 }
1376
1377 fn name(&self, node: NodeId) -> Option<String> {
1378 match self.decode(node) {
1379 Some((c, 0)) => Some(self.containers()[c].name.clone()),
1380 Some((c, v)) => Some(self.containers()[c].role_of(v).to_string()),
1384 None => self.base.name(node),
1385 }
1386 }
1387
1388 fn parent(&self, node: NodeId) -> Option<NodeId> {
1389 match self.decode(node) {
1390 Some((_, 0)) => Some(self.base.root()),
1391 Some((c, _)) => Some(Self::container_node(c)),
1392 None => self.base.parent(node),
1393 }
1394 }
1395
1396 fn traits(&self, node: NodeId) -> Vec<String> {
1397 let mut out = match self.decode(node) {
1398 Some((c, v)) if v > 0 => {
1401 let mut out = vec![self.containers()[c].trait_of(v).to_string()];
1402 if let Some(base) = self.aliased(node) {
1405 out.extend(self.base.traits(base));
1406 }
1407 out
1408 }
1409 Some(_) => Vec::new(),
1410 None => self.base.traits(node),
1411 };
1412 if !self.model.traits.is_empty() {
1414 out.extend(self.declared_traits_of(node));
1415 }
1416 out
1417 }
1418
1419 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
1420 match self.decode(node) {
1421 Some((_, v)) if v > 0 => match self.aliased(node) {
1427 Some(base) => self.base.property(base, name),
1428 None => None,
1429 },
1430 Some(_) => None,
1431 None => self.base.property(node, name),
1432 }
1433 }
1434
1435 fn default_value(&self, node: NodeId) -> Option<Value> {
1436 match self.decode(node) {
1437 Some((_, v)) if v > 0 => match self.aliased(node) {
1438 Some(base) => self.base.default_value(base),
1439 None => self.elevated(node),
1440 },
1441 Some(_) => None,
1442 None => self.base.default_value(node),
1443 }
1444 }
1445
1446 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
1447 match self.decode(node) {
1448 Some((c, 0)) if key == "n-rows" => {
1449 Some(Value::Int(self.containers()[c].members.len() as i64))
1450 }
1451 Some(_) => None,
1452 None => self.base.metadata(node, key),
1453 }
1454 }
1455
1456 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
1461 match self.decode(node) {
1462 Some(_) => &[],
1463 None => self.base.aliased_metadata(node),
1464 }
1465 }
1466
1467 fn provenance(&self, node: NodeId) -> quarb::Provenance {
1474 match self.decode(node) {
1475 Some((c, _)) => match self.aliased(node) {
1476 Some(base) => self.base.provenance(base),
1477 None => quarb::Provenance {
1478 source: Some(format!("model:/{}", self.containers()[c].name)),
1479 ..Default::default()
1480 },
1481 },
1482 None => self.base.provenance(node),
1483 }
1484 }
1485 fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
1486 match self.decode(node) {
1487 Some((_, _)) => match self.aliased(node) {
1488 Some(base) => self.base.provenance_list(base),
1489 None => quarb::ProvenanceList::single(self.provenance(node)),
1490 },
1491 None => self.base.provenance_list(node),
1492 }
1493 }
1494
1495 fn answers_to(&self, node: NodeId, name: &str) -> bool {
1498 if self.decode(node).is_none() && self.base.answers_to(node, name) {
1499 return true;
1500 }
1501 let Some(canonical) = self.name(node) else {
1502 return canonical_none(name);
1503 };
1504 if canonical == name {
1505 return true;
1506 }
1507 let aliases = self
1508 .model
1509 .aliases
1510 .iter()
1511 .filter(|a| a.kind == AliasKind::Name);
1512 for a in aliases {
1513 if a.admits(name, &canonical) {
1514 return true;
1515 }
1516 if self.decode(node).is_none()
1520 && let AliasRule::Exact { alias, original } = &a.rule
1521 && alias == name
1522 && self.base.answers_to(node, original)
1523 {
1524 return true;
1525 }
1526 }
1527 false
1528 }
1529
1530 fn has_trait(&self, node: NodeId, name: &str) -> bool {
1533 let traits = self.traits(node);
1534 if traits.iter().any(|t| t == name) {
1535 return true;
1536 }
1537 self.model
1538 .aliases
1539 .iter()
1540 .filter(|a| a.kind == AliasKind::Trait)
1541 .any(|a| traits.iter().any(|t| a.admits(name, t)))
1542 }
1543
1544 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
1545 let under = self.aliased(node).unwrap_or(node);
1547 if let Some(&target) = self.fabric().resolve.get(&(under, property.to_string())) {
1548 return Some(target);
1549 }
1550 if self.decode(under).is_none() {
1551 return self.base.resolve(under, property, hint);
1552 }
1553 None
1558 }
1559
1560 fn external_ref(
1561 &self,
1562 node: NodeId,
1563 property: &str,
1564 hint: Option<&str>,
1565 ) -> Option<quarb::ExternalRef> {
1566 let under = self.aliased(node).unwrap_or(node);
1570 if self.decode(under).is_none() {
1571 return self.base.external_ref(under, property, hint);
1572 }
1573 None
1574 }
1575
1576 fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
1577 let under = self.aliased(node).unwrap_or(node);
1578 if self.decode(under).is_none() {
1579 return self.base.resolve_fragment(under, fragment);
1580 }
1581 None
1582 }
1583
1584 fn ref_property(&self, node: NodeId) -> Option<String> {
1585 let under = self.aliased(node).unwrap_or(node);
1586 if self.decode(under).is_none() {
1587 return self.base.ref_property(under);
1588 }
1589 None
1590 }
1591
1592 fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
1593 let under = self.aliased(node).unwrap_or(node);
1594 if self.decode(under).is_none() {
1595 return self.base.ref_label(under, property);
1596 }
1597 None
1598 }
1599
1600 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
1601 let f = self.fabric();
1602 match self.decode(node) {
1603 Some((_, v)) if v > 0 => {
1606 let mut out = f.edges.get(&node).cloned().unwrap_or_default();
1607 if let Some(rels) = f.rel_fwd.get(&node) {
1608 out.extend(rels.iter().cloned());
1609 }
1610 if let Some(base) = self.aliased(node) {
1611 out.extend(self.base.links(base));
1612 if let Some(refs) = f.ref_fwd.get(&base) {
1613 out.extend(refs.iter().cloned());
1614 }
1615 }
1616 out
1617 }
1618 Some(_) => Vec::new(),
1619 None => {
1622 let mut out = self.base.links(node);
1623 if let Some(refs) = f.ref_fwd.get(&node) {
1624 out.extend(refs.iter().cloned());
1625 }
1626 if let Some(rels) = f.rel_fwd.get(&node) {
1627 out.extend(rels.iter().cloned());
1628 }
1629 out
1630 }
1631 }
1632 }
1633
1634 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
1635 let f = self.fabric();
1636 match self.decode(node) {
1637 Some((_, v)) if v > 0 => {
1641 let mut out = f.ref_back.get(&node).cloned().unwrap_or_default();
1642 if let Some(e) = f.edges.get(&node) {
1643 out.extend(e.iter().cloned());
1644 }
1645 if let Some(rels) = f.rel_back.get(&node) {
1646 out.extend(rels.iter().cloned());
1647 }
1648 out
1649 }
1650 Some(_) => Vec::new(),
1651 None => {
1655 let mut out = self.base.backlinks(node);
1656 if let Some(refs) = f.ref_back.get(&node) {
1657 out.extend(refs.iter().cloned());
1658 }
1659 if let Some(rels) = f.rel_back.get(&node) {
1660 out.extend(rels.iter().cloned());
1661 }
1662 out
1663 }
1664 }
1665 }
1666
1667 fn document_by_ref(&self, id: &str) -> Option<NodeId> {
1670 self.base.document_by_ref(id)
1671 }
1672
1673 fn reverse_resolve(
1677 &self,
1678 node: NodeId,
1679 property: Option<&str>,
1680 hint: Option<&str>,
1681 ) -> Option<Vec<NodeId>> {
1682 if let Some(field) = property
1686 && self.model.refs.iter().any(|r| r.field == field)
1687 {
1688 let f = self.fabric();
1689 let target = f.alias.get(&node).map_or(node, |(alias, _)| *alias);
1690 let mut out = f
1691 .sources
1692 .get(&(target, field.to_string()))
1693 .cloned()
1694 .unwrap_or_default();
1695 if target != node
1696 && let Some(more) = f.sources.get(&(node, field.to_string()))
1697 {
1698 for n in more {
1699 if !out.contains(n) {
1700 out.push(*n);
1701 }
1702 }
1703 }
1704 return Some(out);
1705 }
1706 if self.decode(node).is_some()
1707 || !self.model.refs.is_empty()
1708 || !self.model.rels.is_empty()
1709 || !self.model.edges.is_empty()
1710 {
1711 return None;
1712 }
1713 self.base.reverse_resolve(node, property, hint)
1714 }
1715
1716 fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
1720 if self.decode(node).is_some()
1721 || node == self.base.root()
1722 || self.model.aliases.iter().any(|a| a.kind == AliasKind::Name)
1723 {
1724 return None;
1725 }
1726 self.base.descendants_named(node, name)
1727 }
1728
1729 fn descendants_where(
1730 &self,
1731 node: NodeId,
1732 name: &str,
1733 property: &str,
1734 value: &Value,
1735 ) -> Option<Vec<(NodeId, usize)>> {
1736 if self.decode(node).is_some() || node == self.base.root() || !self.model.aliases.is_empty()
1737 {
1738 return None;
1739 }
1740 self.base.descendants_where(node, name, property, value)
1741 }
1742
1743 fn select_plan(&self, scope: &[&str], plan: &quarb::StepPlan<'_>) -> Option<Vec<bool>> {
1744 if !self.model.aliases.is_empty()
1746 || scope
1747 .first()
1748 .is_some_and(|c| self.container_by_name(c).is_some())
1749 {
1750 return None;
1751 }
1752 self.base.select_plan(scope, plan)
1753 }
1754
1755 fn estimate(&self, scope: &[&str], plan: &quarb::StepPlan<'_>) -> Option<quarb::Estimate> {
1756 if !self.model.aliases.is_empty()
1757 || scope
1758 .first()
1759 .is_some_and(|c| self.container_by_name(c).is_some())
1760 {
1761 return None;
1762 }
1763 self.base.estimate(scope, plan)
1764 }
1765
1766 fn select(&self, from: NodeId, plan: &quarb::StepPlan<'_>) -> Option<quarb::Selection> {
1767 match plan.axis {
1772 quarb::PlanAxis::Child => quarb::adapter::select_by_hooks(self, from, plan),
1773 quarb::PlanAxis::Descendant => {
1774 if self.decode(from).is_some()
1775 || from == self.base.root()
1776 || !self.model.aliases.is_empty()
1777 {
1778 return None;
1779 }
1780 self.base.select(from, plan)
1781 }
1782 }
1783 }
1784
1785 fn has_node(&self, node: NodeId) -> bool {
1786 self.decode(node).is_some() || self.base.has_node(node)
1787 }
1788
1789 fn link_property(
1793 &self,
1794 source: NodeId,
1795 label: &str,
1796 target: NodeId,
1797 name: &str,
1798 ) -> Option<Value> {
1799 let base_of = |n: NodeId| match self.decode(n) {
1800 None => Some(n),
1801 Some(_) => self.aliased(n),
1802 };
1803 self.base
1804 .link_property(base_of(source)?, label, base_of(target)?, name)
1805 }
1806
1807 fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
1808 let base: Vec<NodeId> = nodes
1809 .iter()
1810 .copied()
1811 .filter(|n| self.decode(*n).is_none())
1812 .collect();
1813 if !base.is_empty() {
1814 self.base.prefetch_links(&base, dir);
1815 }
1816 }
1817
1818 fn quantifier_bound(&self) -> usize {
1819 self.base.quantifier_bound()
1820 }
1821 fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
1824 if let Ok(customs) = &self.units
1825 && !customs.is_empty()
1826 && let Some(hit) = kaiv::unit::scale_with(expr, customs)
1827 {
1828 return Some(hit);
1829 }
1830 self.base.unit_scale(expr)
1831 }
1832}
1833
1834fn canonical_none(_name: &str) -> bool {
1836 false
1837}
1838
1839fn scope_role(scope: &str) -> String {
1845 scope
1846 .split('/')
1847 .filter(|seg| {
1848 !seg.is_empty()
1849 && !seg.starts_with('*')
1850 && !seg.starts_with('[')
1851 && !seg.chars().next().is_some_and(|c| c.is_ascii_digit())
1852 })
1853 .next_back()
1854 .unwrap_or("")
1855 .split(['[', ':'])
1856 .next()
1857 .unwrap_or("")
1858 .to_string()
1859}
1860
1861pub struct Borrowed<'a>(pub &'a dyn AstAdapter);
1867
1868impl AstAdapter for Borrowed<'_> {
1869 quarb::forward_ast_adapter!(0);
1870}