1mod parse;
18
19pub use parse::{
20 parse_model, resolve_mount_target, EdgeDecl, Model, Mount, NodeDecl, RefDecl, RelDecl,
21};
22
23use quarb::{AstAdapter, NodeId, QueryResult, Value};
24use std::cell::OnceCell;
25use std::collections::HashMap;
26
27const MODEL_TAG: u64 = 1 << 63;
32const CIDX_SHIFT: u64 = 44;
33const VAL_MASK: u64 = (1 << CIDX_SHIFT) - 1;
34
35enum Members {
39 Values(Vec<Value>),
42 Nodes(Vec<NodeId>),
47}
48
49impl Members {
50 fn len(&self) -> usize {
51 match self {
52 Members::Values(v) => v.len(),
53 Members::Nodes(n) => n.len(),
54 }
55 }
56}
57
58struct Container {
60 name: String,
61 role: String,
64 trait_name: String,
66 members: Members,
70 by_str: HashMap<String, usize>,
72}
73
74enum Member {
76 Value(Value),
77 Node(NodeId),
78}
79
80impl Container {
81 fn member(&self, v: usize) -> Option<Member> {
83 if v == 0 {
84 return None;
85 }
86 match &self.members {
87 Members::Values(vs) => vs.get(v - 1).cloned().map(Member::Value),
88 Members::Nodes(ns) => ns.get(v - 1).copied().map(Member::Node),
89 }
90 }
91}
92
93struct Fabric {
96 alias: HashMap<NodeId, (NodeId, String)>,
101 resolve: HashMap<(NodeId, String), NodeId>,
103 ref_back: HashMap<NodeId, Vec<(String, NodeId)>>,
106 ref_fwd: HashMap<NodeId, Vec<(String, NodeId)>>,
108 rel_fwd: HashMap<NodeId, Vec<(String, NodeId)>>,
112 rel_back: HashMap<NodeId, Vec<(String, NodeId)>>,
113 edges: HashMap<NodeId, Vec<(String, NodeId)>>,
117}
118
119pub struct ModelAdapter<A: AstAdapter> {
121 base: A,
122 model: Model,
123 containers: OnceCell<Vec<Container>>,
124 fabric: OnceCell<Fabric>,
125}
126
127impl<A: AstAdapter> ModelAdapter<A> {
128 pub fn new(base: A, model: Model) -> Self {
129 ModelAdapter {
130 base,
131 model,
132 containers: OnceCell::new(),
133 fabric: OnceCell::new(),
134 }
135 }
136
137 pub fn base(&self) -> &A {
138 &self.base
139 }
140
141 pub fn locator(&self, node: NodeId, base_locator: impl Fn(NodeId) -> String) -> String {
144 match self.decode(node) {
145 None => base_locator(node),
146 Some((c, 0)) => format!("/{}", self.containers()[c].name),
147 Some((c, v)) => {
148 let cont = &self.containers()[c];
151 format!("/{}/{}[{}]", cont.name, cont.role, v)
152 }
153 }
154 }
155
156 fn seeded_defs(&self) -> quarb::Defs {
157 quarb::parse_defs(&self.model.defs_text).unwrap_or_default()
158 }
159
160 fn containers(&self) -> &[Container] {
165 self.containers.get_or_init(|| {
166 let defs = self.seeded_defs();
167 let mut built: Vec<Container> = Vec::new();
168 for decl in &self.model.nodes {
169 let members = self.members(&decl.query, &defs, &built);
170 let by_str = match &members {
171 Members::Values(vs) => vs
172 .iter()
173 .enumerate()
174 .map(|(i, v)| (v.to_string(), i))
175 .collect(),
176 Members::Nodes(ns) => ns
180 .iter()
181 .enumerate()
182 .filter_map(|(i, n)| self.base_key(*n).map(|k| (k, i)))
183 .collect(),
184 };
185 built.push(Container {
186 name: decl.name.clone(),
187 role: decl.role.clone(),
188 trait_name: decl.trait_name.clone(),
189 members,
190 by_str,
191 });
192 }
193 built
194 })
195 }
196
197 fn members(&self, query: &str, defs: &quarb::Defs, prior: &[Container]) -> Members {
204 let result = if prior.is_empty() {
208 quarb::run_with_defs(query, defs, &self.base)
209 } else {
210 let scratch = PriorView {
211 base: &self.base,
212 prior,
213 };
214 quarb::run_with_defs(query, defs, &scratch)
215 };
216 match result {
217 Ok(QueryResult::Values(vs)) => {
218 let mut seen = std::collections::HashSet::new();
219 Members::Values(vs.into_iter().filter(|v| seen.insert(v.to_string())).collect())
220 }
221 Ok(QueryResult::Nodes(ns)) => {
222 let mut seen = std::collections::HashSet::new();
223 Members::Nodes(ns.into_iter().filter(|n| seen.insert(*n)).collect())
224 }
225 Err(_) => Members::Values(Vec::new()),
226 }
227 }
228
229 fn base_key(&self, node: NodeId) -> Option<String> {
232 self.base
233 .default_value(node)
234 .map(|v| v.to_string())
235 .or_else(|| self.base.name(node))
236 }
237
238 fn aliased(&self, node: NodeId) -> Option<NodeId> {
240 match self.decode(node) {
241 Some((c, v)) if v > 0 => match &self.containers()[c].members {
242 Members::Nodes(ns) => ns.get(v - 1).copied(),
243 Members::Values(_) => None,
244 },
245 _ => None,
246 }
247 }
248
249 fn elevated(&self, node: NodeId) -> Option<Value> {
251 match self.decode(node) {
252 Some((c, v)) if v > 0 => match &self.containers()[c].members {
253 Members::Values(vs) => vs.get(v - 1).cloned(),
254 Members::Nodes(_) => None,
255 },
256 _ => None,
257 }
258 }
259
260 fn container_node(c: usize) -> NodeId {
261 NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT)
262 }
263
264 fn value_node(c: usize, v: usize) -> NodeId {
265 NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (v as u64 + 1))
266 }
267
268 fn decode(&self, node: NodeId) -> Option<(usize, usize)> {
272 if node.0 & MODEL_TAG == 0 {
273 return None;
274 }
275 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
276 let v = (node.0 & VAL_MASK) as usize;
277 (c < self.containers().len()).then_some((c, v))
278 }
279
280 fn container_by_name(&self, name: &str) -> Option<usize> {
281 self.containers().iter().position(|c| c.name == name)
282 }
283
284 fn find_value(&self, container: usize, value: &Value) -> Option<NodeId> {
286 let idx = *self.containers()[container].by_str.get(&value.to_string())?;
287 Some(Self::value_node(container, idx))
288 }
289
290 fn fabric(&self) -> &Fabric {
292 self.fabric.get_or_init(|| {
293 let defs = self.seeded_defs();
294 let mut f = Fabric {
295 alias: HashMap::new(),
296 resolve: HashMap::new(),
297 ref_back: HashMap::new(),
298 ref_fwd: HashMap::new(),
299 rel_fwd: HashMap::new(),
300 rel_back: HashMap::new(),
301 edges: HashMap::new(),
302 };
303 for (c, cont) in self.containers().iter().enumerate() {
304 if let Members::Nodes(ns) = &cont.members {
305 for (i, n) in ns.iter().enumerate() {
306 f.alias
307 .entry(*n)
308 .or_insert((Self::value_node(c, i), cont.role.clone()));
309 }
310 }
311 }
312 for decl in &self.model.refs {
315 let Some(container) = self.container_by_name(&decl.container) else {
316 continue;
317 };
318 let by_key: Option<HashMap<String, NodeId>> =
322 decl.key_field.as_deref().map(|f| {
323 (1..=self.containers()[container].members.len())
324 .filter_map(|v| {
325 let n = Self::value_node(container, v - 1);
326 self.property(n, f).map(|k| (k.to_string(), n))
327 })
328 .collect()
329 });
330 for node in self.scope_nodes(&decl.scope, &defs) {
331 let Some(value) = self.base.property(node, &decl.field) else {
332 continue;
333 };
334 if matches!(value, Value::Null) {
335 continue;
336 }
337 let target = match &by_key {
338 Some(idx) => idx.get(&value.to_string()).copied(),
339 None => self.find_value(container, &value),
340 };
341 let Some(target) = target else {
342 continue;
343 };
344 let fwd = self.containers()[container].role.clone();
352 let (back_node, back) = match f.alias.get(&node) {
356 Some((alias, role)) => (*alias, role.clone()),
357 None => (node, scope_role(&decl.scope)),
358 };
359 f.resolve.insert((node, decl.field.clone()), target);
360 f.ref_fwd.entry(node).or_default().push((fwd, target));
361 f.ref_back.entry(target).or_default().push((back, back_node));
362 }
363 }
364 for decl in &self.model.rels {
371 let fwd = scope_role(&decl.target);
372 let back = scope_role(&decl.source);
373 for source in self.view_nodes(&decl.source, &defs) {
374 let cond = self.bind_driver(&decl.cond, source);
375 let query = format!("{}{}", decl.target, cond);
376 for target in self.view_nodes(&query, &defs) {
377 if target == source {
378 continue;
379 }
380 f.rel_fwd
381 .entry(source)
382 .or_default()
383 .push((fwd.clone(), target));
384 f.rel_back
385 .entry(target)
386 .or_default()
387 .push((back.clone(), source));
388 }
389 }
390 }
391 for decl in &self.model.edges {
395 let ca = self.field_container(&decl.field_a);
396 let cb = self.field_container(&decl.field_b);
397 let (Some((ca, la)), Some((cb, lb))) = (ca, cb) else {
398 continue;
399 };
400 let mut seen = std::collections::HashSet::new();
401 for node in self.scope_nodes(&decl.scope, &defs) {
402 let (Some(va), Some(vb)) = (
403 self.base.property(node, &decl.field_a),
404 self.base.property(node, &decl.field_b),
405 ) else {
406 continue;
407 };
408 if matches!(va, Value::Null) || matches!(vb, Value::Null) {
409 continue;
410 }
411 let (Some(na), Some(nb)) =
412 (self.find_value(ca, &va), self.find_value(cb, &vb))
413 else {
414 continue;
415 };
416 if seen.insert((na, nb)) {
417 f.edges.entry(na).or_default().push((lb.clone(), nb));
418 f.edges.entry(nb).or_default().push((la.clone(), na));
419 }
420 }
421 }
422 f
423 })
424 }
425
426 fn field_container(&self, field: &str) -> Option<(usize, String)> {
431 let decl = self.model.refs.iter().find(|r| r.field == field)?;
432 let c = self.container_by_name(&decl.container)?;
433 Some((c, self.containers()[c].role.clone()))
434 }
435
436 fn scope_nodes(&self, scope: &str, defs: &quarb::Defs) -> Vec<NodeId> {
438 match quarb::run_with_defs(scope, defs, &self.base) {
439 Ok(QueryResult::Nodes(ns)) => ns,
440 _ => Vec::new(),
441 }
442 }
443
444 fn view_nodes(&self, path: &str, defs: &quarb::Defs) -> Vec<NodeId> {
449 let view = PriorView {
450 base: &self.base,
451 prior: self.containers(),
452 };
453 match quarb::run_with_defs(path, defs, &view) {
454 Ok(QueryResult::Nodes(ns)) => ns,
455 _ => Vec::new(),
456 }
457 }
458
459 fn bind_driver(&self, cond: &str, source: NodeId) -> String {
464 let view = PriorView {
465 base: &self.base,
466 prior: self.containers(),
467 };
468 let b: Vec<char> = cond.chars().collect();
469 let mut out = String::new();
470 let mut i = 0;
471 while i < b.len() {
472 match b[i] {
473 q @ ('\'' | '"') => {
474 out.push(q);
475 i += 1;
476 while i < b.len() && b[i] != q {
477 if b[i] == '\\' && i + 1 < b.len() {
478 out.push(b[i]);
479 i += 1;
480 }
481 out.push(b[i]);
482 i += 1;
483 }
484 if i < b.len() {
485 out.push(b[i]);
486 i += 1;
487 }
488 }
489 c @ ('$' | '_')
492 if (c == '$' && b.get(i + 1) == Some(&'$'))
493 || (c == '_'
494 && !(i > 0 && (b[i - 1].is_alphanumeric() || b[i - 1] == '_' || b[i - 1] == '-'))
495 && !b
496 .get(i + 1)
497 .is_some_and(|c| c.is_alphanumeric() || *c == '_' || *c == '-')) =>
498 {
499 if c == '$' {
500 i += 2;
501 if b.get(i) == Some(&'_')
502 && !b
503 .get(i + 1)
504 .is_some_and(|c| c.is_alphanumeric() || *c == '_' || *c == '-')
505 {
506 i += 1;
507 }
508 } else {
509 i += 1;
510 }
511 let value = if b.get(i) == Some(&':') && b.get(i + 1) == Some(&':') {
512 i += 2;
513 let start = i;
514 while i < b.len()
515 && (b[i].is_alphanumeric() || b[i] == '_' || b[i] == '-')
516 {
517 i += 1;
518 }
519 let field: String = b[start..i].iter().collect();
520 view.property(source, &field)
521 } else {
522 view.default_value(source)
523 };
524 out.push_str(&literal(value.unwrap_or(Value::Null)));
525 }
526 c => {
527 out.push(c);
528 i += 1;
529 }
530 }
531 }
532 out
533 }
534}
535
536fn literal(v: Value) -> String {
539 match v {
540 Value::Int(n) => n.to_string(),
541 Value::Float(f) => f.to_string(),
542 Value::Bool(b) => b.to_string(),
543 Value::Null => "''".to_string(),
544 other => format!("'{}'", other.to_string().replace('\\', "\\\\").replace('\'', "\\'")),
545 }
546}
547
548struct PriorView<'a, A: AstAdapter> {
552 base: &'a A,
553 prior: &'a [Container],
554}
555
556impl<A: AstAdapter> AstAdapter for PriorView<'_, A> {
557 fn root(&self) -> NodeId {
558 self.base.root()
559 }
560 fn children(&self, node: NodeId) -> Vec<NodeId> {
561 if node.0 & MODEL_TAG != 0 {
562 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
563 let v = (node.0 & VAL_MASK) as usize;
564 if v == 0 && c < self.prior.len() {
565 return (0..self.prior[c].members.len())
566 .map(|i| NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (i as u64 + 1)))
567 .collect();
568 }
569 return Vec::new();
570 }
571 let mut kids = self.base.children(node);
572 if node == self.base.root() {
573 for c in 0..self.prior.len() {
574 kids.push(NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT));
575 }
576 }
577 kids
578 }
579 fn name(&self, node: NodeId) -> Option<String> {
580 if node.0 & MODEL_TAG != 0 {
581 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
582 let v = (node.0 & VAL_MASK) as usize;
583 let cont = self.prior.get(c)?;
584 return Some(if v == 0 {
587 cont.name.clone()
588 } else {
589 cont.role.clone()
590 });
591 }
592 self.base.name(node)
593 }
594 fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
595 if node == self.base.root() {
596 let mut out = self.base.children_named(node, name);
597 if let Some(c) = self.prior.iter().position(|k| k.name == name) {
598 out.push(NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT));
599 }
600 return out;
601 }
602 if node.0 & MODEL_TAG != 0 {
603 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
604 let v = (node.0 & VAL_MASK) as usize;
605 let Some(cont) = self.prior.get(c) else {
606 return Vec::new();
607 };
608 if v == 0 && name == cont.role {
609 return (0..cont.members.len())
610 .map(|i| NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT | (i as u64 + 1)))
611 .collect();
612 }
613 return Vec::new();
614 }
615 self.base.children_named(node, name)
616 }
617 fn traits(&self, node: NodeId) -> Vec<String> {
618 if node.0 & MODEL_TAG != 0 {
619 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
620 let v = (node.0 & VAL_MASK) as usize;
621 return match self.prior.get(c) {
622 Some(cont) if v > 0 => vec![cont.trait_name.clone()],
623 _ => Vec::new(),
624 };
625 }
626 self.base.traits(node)
627 }
628 fn parent(&self, node: NodeId) -> Option<NodeId> {
629 if node.0 & MODEL_TAG != 0 {
630 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
631 let v = (node.0 & VAL_MASK) as usize;
632 return Some(if v == 0 {
633 self.base.root()
634 } else {
635 NodeId(MODEL_TAG | (c as u64) << CIDX_SHIFT)
636 });
637 }
638 self.base.parent(node)
639 }
640 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
641 if node.0 & MODEL_TAG != 0 {
642 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
643 let v = (node.0 & VAL_MASK) as usize;
644 return match self.prior.get(c)?.member(v)? {
645 Member::Value(_) => None,
648 Member::Node(n) => self.base.property(n, name),
649 };
650 }
651 self.base.property(node, name)
652 }
653 fn default_value(&self, node: NodeId) -> Option<Value> {
654 if node.0 & MODEL_TAG != 0 {
655 let c = ((node.0 & !MODEL_TAG) >> CIDX_SHIFT) as usize;
656 let v = (node.0 & VAL_MASK) as usize;
657 return match self.prior.get(c)?.member(v)? {
658 Member::Value(val) => Some(val),
659 Member::Node(n) => self.base.default_value(n),
660 };
661 }
662 self.base.default_value(node)
663 }
664 fn provenance(&self, node: NodeId) -> quarb::Provenance {
668 if node.0 & MODEL_TAG != 0 {
669 return quarb::Provenance::default();
670 }
671 self.base.provenance(node)
672 }
673}
674
675impl<A: AstAdapter> AstAdapter for ModelAdapter<A> {
676 fn root(&self) -> NodeId {
677 self.base.root()
678 }
679
680 fn children(&self, node: NodeId) -> Vec<NodeId> {
681 match self.decode(node) {
682 Some((c, 0)) => (0..self.containers()[c].members.len())
684 .map(|v| Self::value_node(c, v))
685 .collect(),
686 Some(_) => match self.aliased(node) {
689 Some(base) => self.base.children(base),
690 None => Vec::new(),
691 },
692 None => {
695 let mut kids = self.base.children(node);
696 if node == self.base.root() {
697 for c in 0..self.containers().len() {
698 kids.push(Self::container_node(c));
699 }
700 }
701 kids
702 }
703 }
704 }
705
706 fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
707 if node == self.base.root() {
709 let mut out = self.base.children_named(node, name);
710 if let Some(c) = self.container_by_name(name) {
711 out.push(Self::container_node(c));
712 }
713 return out;
714 }
715 match self.decode(node) {
716 Some((c, 0)) => {
719 let cont = &self.containers()[c];
720 if name == cont.role {
721 (0..cont.members.len()).map(|v| Self::value_node(c, v)).collect()
722 } else {
723 Vec::new()
724 }
725 }
726 Some(_) => match self.aliased(node) {
727 Some(base) => self.base.children_named(base, name),
728 None => Vec::new(),
729 },
730 None => self.base.children_named(node, name),
731 }
732 }
733
734 fn name(&self, node: NodeId) -> Option<String> {
735 match self.decode(node) {
736 Some((c, 0)) => Some(self.containers()[c].name.clone()),
737 Some((c, _)) => Some(self.containers()[c].role.clone()),
741 None => self.base.name(node),
742 }
743 }
744
745 fn parent(&self, node: NodeId) -> Option<NodeId> {
746 match self.decode(node) {
747 Some((_, 0)) => Some(self.base.root()),
748 Some((c, _)) => Some(Self::container_node(c)),
749 None => self.base.parent(node),
750 }
751 }
752
753 fn traits(&self, node: NodeId) -> Vec<String> {
754 match self.decode(node) {
755 Some((c, v)) if v > 0 => {
758 let mut out = vec![self.containers()[c].trait_name.clone()];
759 if let Some(base) = self.aliased(node) {
762 out.extend(self.base.traits(base));
763 }
764 out
765 }
766 Some(_) => Vec::new(),
767 None => self.base.traits(node),
768 }
769 }
770
771 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
772 match self.decode(node) {
773 Some((_, v)) if v > 0 => match self.aliased(node) {
779 Some(base) => self.base.property(base, name),
780 None => None,
781 },
782 Some(_) => None,
783 None => self.base.property(node, name),
784 }
785 }
786
787 fn default_value(&self, node: NodeId) -> Option<Value> {
788 match self.decode(node) {
789 Some((_, v)) if v > 0 => match self.aliased(node) {
790 Some(base) => self.base.default_value(base),
791 None => self.elevated(node),
792 },
793 Some(_) => None,
794 None => self.base.default_value(node),
795 }
796 }
797
798 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
799 match self.decode(node) {
800 Some((c, 0)) if key == "n-rows" => {
801 Some(Value::Int(self.containers()[c].members.len() as i64))
802 }
803 Some(_) => None,
804 None => self.base.metadata(node, key),
805 }
806 }
807
808 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
811 self.base.aliased_metadata(node)
812 }
813
814 fn provenance(&self, node: NodeId) -> quarb::Provenance {
821 match self.decode(node) {
822 Some((c, _)) => match self.aliased(node) {
823 Some(base) => self.base.provenance(base),
824 None => quarb::Provenance {
825 source: Some(format!("model:/{}", self.containers()[c].name)),
826 ..Default::default()
827 },
828 },
829 None => self.base.provenance(node),
830 }
831 }
832
833 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
834 let under = self.aliased(node).unwrap_or(node);
836 if let Some(&target) = self.fabric().resolve.get(&(under, property.to_string())) {
837 return Some(target);
838 }
839 if self.decode(under).is_none() {
840 return self.base.resolve(under, property, hint);
841 }
842 None
847 }
848
849 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
850 let f = self.fabric();
851 match self.decode(node) {
852 Some((_, v)) if v > 0 => {
855 let mut out = f.edges.get(&node).cloned().unwrap_or_default();
856 if let Some(rels) = f.rel_fwd.get(&node) {
857 out.extend(rels.iter().cloned());
858 }
859 if let Some(base) = self.aliased(node) {
860 out.extend(self.base.links(base));
861 if let Some(refs) = f.ref_fwd.get(&base) {
862 out.extend(refs.iter().cloned());
863 }
864 }
865 out
866 }
867 Some(_) => Vec::new(),
868 None => {
871 let mut out = self.base.links(node);
872 if let Some(refs) = f.ref_fwd.get(&node) {
873 out.extend(refs.iter().cloned());
874 }
875 if let Some(rels) = f.rel_fwd.get(&node) {
876 out.extend(rels.iter().cloned());
877 }
878 out
879 }
880 }
881 }
882
883 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
884 let f = self.fabric();
885 match self.decode(node) {
886 Some((_, v)) if v > 0 => {
890 let mut out = f.ref_back.get(&node).cloned().unwrap_or_default();
891 if let Some(e) = f.edges.get(&node) {
892 out.extend(e.iter().cloned());
893 }
894 if let Some(rels) = f.rel_back.get(&node) {
895 out.extend(rels.iter().cloned());
896 }
897 out
898 }
899 Some(_) => Vec::new(),
900 None => {
901 let mut out = self.base.backlinks(node);
902 if let Some(rels) = f.rel_back.get(&node) {
903 out.extend(rels.iter().cloned());
904 }
905 out
906 }
907 }
908 }
909
910 fn quantifier_bound(&self) -> usize {
911 self.base.quantifier_bound()
912 }
913 fn allow_shell(&self) -> bool {
914 self.base.allow_shell()
915 }
916 fn invocation_instant(&self) -> Option<(i64, u32)> {
917 self.base.invocation_instant()
918 }
919 fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
920 self.base.unit_scale(expr)
921 }
922}
923
924fn scope_role(scope: &str) -> String {
930 scope
931 .split('/')
932 .filter(|seg| {
933 !seg.is_empty()
934 && !seg.starts_with('*')
935 && !seg.starts_with('[')
936 && !seg.chars().next().is_some_and(|c| c.is_ascii_digit())
937 })
938 .next_back()
939 .unwrap_or("")
940 .split(['[', ':'])
941 .next()
942 .unwrap_or("")
943 .to_string()
944}
945
946pub struct Borrowed<'a>(pub &'a dyn AstAdapter);
952
953impl AstAdapter for Borrowed<'_> {
954 fn root(&self) -> NodeId {
955 self.0.root()
956 }
957 fn children(&self, n: NodeId) -> Vec<NodeId> {
958 self.0.children(n)
959 }
960 fn name(&self, n: NodeId) -> Option<String> {
961 self.0.name(n)
962 }
963 fn parent(&self, n: NodeId) -> Option<NodeId> {
964 self.0.parent(n)
965 }
966 fn traits(&self, n: NodeId) -> Vec<String> {
967 self.0.traits(n)
968 }
969 fn children_named(&self, n: NodeId, name: &str) -> Vec<NodeId> {
970 self.0.children_named(n, name)
971 }
972 fn property(&self, n: NodeId, name: &str) -> Option<Value> {
973 self.0.property(n, name)
974 }
975 fn default_value(&self, n: NodeId) -> Option<Value> {
976 self.0.default_value(n)
977 }
978 fn metadata(&self, n: NodeId, key: &str) -> Option<Value> {
979 self.0.metadata(n, key)
980 }
981 fn links(&self, n: NodeId) -> Vec<(String, NodeId)> {
982 self.0.links(n)
983 }
984 fn backlinks(&self, n: NodeId) -> Vec<(String, NodeId)> {
985 self.0.backlinks(n)
986 }
987 fn resolve(&self, n: NodeId, p: &str, h: Option<&str>) -> Option<NodeId> {
988 self.0.resolve(n, p, h)
989 }
990 fn link_property(&self, s: NodeId, l: &str, t: NodeId, name: &str) -> Option<Value> {
991 self.0.link_property(s, l, t, name)
992 }
993 fn quantifier_bound(&self) -> usize {
994 self.0.quantifier_bound()
995 }
996 fn allow_shell(&self) -> bool {
997 self.0.allow_shell()
998 }
999 fn invocation_instant(&self) -> Option<(i64, u32)> {
1000 self.0.invocation_instant()
1001 }
1002 fn provenance(&self, n: NodeId) -> quarb::Provenance {
1003 self.0.provenance(n)
1004 }
1005 fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
1006 self.0.unit_scale(expr)
1007 }
1008}