1use quarb::{AstAdapter, NodeId, Value};
34use std::cell::RefCell;
35use std::collections::HashMap;
36use std::path::PathBuf;
37
38enum Inner {
40 Json(quarb_json::JsonAdapter),
41 Xml(quarb_xml::XmlAdapter),
42 Html(quarb_html::HtmlAdapter),
43 Text(quarb_text::TextModel),
44 Csv(quarb_csv::CsvAdapter),
45 #[cfg(feature = "source")]
46 Syntax(quarb_tree_sitter::TreeSitterAdapter),
47 #[cfg(feature = "source")]
48 Code(quarb_code::CodeModel),
49 Archive(Box<ComposeAdapter<quarb_archive::ArchiveAdapter>>),
52}
53
54impl Inner {
55 fn adapter(&self) -> &dyn AstAdapter {
56 match self {
57 Inner::Json(a) => a,
58 Inner::Xml(a) => a,
59 Inner::Html(a) => a,
60 Inner::Text(a) => a,
61 Inner::Csv(a) => a,
62 #[cfg(feature = "source")]
63 Inner::Syntax(a) => a,
64 #[cfg(feature = "source")]
65 Inner::Code(a) => a,
66 Inner::Archive(a) => &**a,
67 }
68 }
69
70 fn locator(&self, node: NodeId) -> String {
71 match self {
72 Inner::Json(a) => a.pointer(node),
73 Inner::Xml(a) => a.locator(node),
74 Inner::Html(a) => a.locator(node),
75 Inner::Text(a) => a.locator(node),
76 Inner::Csv(a) => a.locator(node),
77 #[cfg(feature = "source")]
78 Inner::Syntax(a) => a.locator(node),
79 #[cfg(feature = "source")]
80 Inner::Code(a) => a.locator(node),
81 Inner::Archive(a) => a.locator(node, |o| a.outer().locator(o)),
82 }
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
93pub enum SourceGraft {
94 #[default]
95 Syntax,
96 Code,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
105pub enum DocumentGraft {
106 #[default]
107 Dom,
108 Text,
109}
110
111fn archive_name(name: &str) -> bool {
115 let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
116 matches!(ext.as_str(), "zip" | "tar" | "tgz" | "gz")
117}
118
119fn parse_inner(name: &str, content: &str, graft: SourceGraft, doc: DocumentGraft) -> Option<Inner> {
121 #[cfg(not(feature = "source"))]
122 let _ = graft;
123 let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
124 match ext.as_str() {
125 "json" => {
126 return quarb_json::JsonAdapter::parse(content)
127 .ok()
128 .map(Inner::Json);
129 }
130 "jsonl" | "ndjson" => {
131 return quarb_json::JsonAdapter::parse_lines(content)
132 .ok()
133 .map(Inner::Json);
134 }
135 "xml" | "svg" | "xhtml" => {
136 return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
137 }
138 "html" | "htm" => {
139 return Some(match doc {
140 DocumentGraft::Dom => Inner::Html(quarb_html::HtmlAdapter::parse(content)),
141 DocumentGraft::Text => Inner::Text(quarb_text_html::parse(content)),
142 });
143 }
144 "yaml" | "yml" => return quarb_yaml::parse(content).ok().map(Inner::Json),
145 "toml" => return quarb_toml::parse(content).ok().map(Inner::Json),
146 "md" | "markdown" => {
147 return Some(match doc {
148 DocumentGraft::Dom => Inner::Html(quarb_markdown::parse(content)),
149 DocumentGraft::Text => Inner::Text(quarb_text_markdown::parse(content)),
150 });
151 }
152 "txt" => return Some(Inner::Text(quarb_text::TextModel::parse_plain(content))),
155 "csv" => return quarb_csv::CsvAdapter::parse(content).ok().map(Inner::Csv),
156 "tsv" => {
157 return quarb_csv::CsvAdapter::parse_with_delimiter(content, b'\t')
158 .ok()
159 .map(Inner::Csv);
160 }
161 #[cfg(feature = "source")]
162 ext if quarb_tree_sitter::supported(ext) => {
163 return match graft {
164 SourceGraft::Syntax => quarb_tree_sitter::TreeSitterAdapter::parse(content, ext)
165 .ok()
166 .map(Inner::Syntax),
167 SourceGraft::Code => quarb_code::CodeModel::parse(content, ext)
168 .ok()
169 .map(Inner::Code),
170 };
171 }
172 _ => {}
173 }
174 let t = content.trim_start();
176 if t.starts_with('{') || t.starts_with('[') {
177 return quarb_json::JsonAdapter::parse(content)
178 .ok()
179 .map(Inner::Json);
180 }
181 if t.starts_with("<?xml") {
182 return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
183 }
184 None
185}
186
187struct Graft {
189 outer: NodeId,
190 inner: Inner,
191}
192
193const GRAFT_BIT: u64 = 1 << 55;
202
203pub type DocumentReader = std::rc::Rc<dyn Fn(&std::path::Path) -> Option<quarb_text::TextModel>>;
207
208pub struct ComposeAdapter<A: AstAdapter> {
209 outer: A,
210 grafts: RefCell<Vec<Graft>>,
211 probed: RefCell<HashMap<NodeId, Option<usize>>>,
214 interned: RefCell<HashMap<(usize, NodeId), NodeId>>,
216 reverse: RefCell<Vec<(usize, NodeId)>>,
217 source_path: Option<fn(&A, NodeId) -> Option<PathBuf>>,
221 source_graft: SourceGraft,
223 document_graft: DocumentGraft,
225 document_url: Option<fn(&A, NodeId) -> Option<String>>,
229 urls: RefCell<Option<HashMap<String, NodeId>>>,
231 document_reader: Option<DocumentReader>,
236}
237
238impl<A: AstAdapter> ComposeAdapter<A> {
239 pub fn new(outer: A) -> Self {
240 ComposeAdapter {
241 outer,
242 grafts: RefCell::new(Vec::new()),
243 probed: RefCell::new(HashMap::new()),
244 interned: RefCell::new(HashMap::new()),
245 reverse: RefCell::new(Vec::new()),
246 source_path: None,
247 source_graft: SourceGraft::default(),
248 document_graft: DocumentGraft::default(),
249 document_url: None,
250 urls: RefCell::new(None),
251 document_reader: None,
252 }
253 }
254
255 pub fn with_document_reader(mut self, reader: DocumentReader) -> Self {
259 self.document_reader = Some(reader);
260 self
261 }
262
263 pub fn with_document_urls(mut self, document_url: fn(&A, NodeId) -> Option<String>) -> Self {
269 self.document_url = Some(document_url);
270 self
271 }
272
273 fn leaf_by_url(&self, url: &str) -> Option<NodeId> {
276 let f = self.document_url?;
277 if self.urls.borrow().is_none() {
278 let mut map = HashMap::new();
279 let mut stack = vec![self.outer.root()];
280 while let Some(n) = stack.pop() {
281 let kids = self.outer.children(n);
282 if kids.is_empty() {
283 if let Some(u) = f(&self.outer, n) {
284 map.entry(u.split('#').next().unwrap_or(&u).to_string())
285 .or_insert(n);
286 }
287 } else {
288 stack.extend(kids);
289 }
290 }
291 *self.urls.borrow_mut() = Some(map);
292 }
293 self.urls.borrow().as_ref()?.get(url).copied()
294 }
295
296 pub fn with_source_graft(mut self, graft: SourceGraft) -> Self {
299 self.source_graft = graft;
300 self
301 }
302
303 pub fn with_document_graft(mut self, graft: DocumentGraft) -> Self {
306 self.document_graft = graft;
307 self
308 }
309
310 pub fn with_source_paths(outer: A, source_path: fn(&A, NodeId) -> Option<PathBuf>) -> Self {
316 ComposeAdapter {
317 source_path: Some(source_path),
318 ..Self::new(outer)
319 }
320 }
321
322 pub fn outer(&self) -> &A {
324 &self.outer
325 }
326
327 pub fn locator(&self, node: NodeId, outer_locator: impl Fn(NodeId) -> String) -> String {
330 match self.split(node) {
331 None => outer_locator(node),
332 Some((g, inner)) => {
333 let grafts = self.grafts.borrow();
334 let graft = &grafts[g];
335 format!(
336 "{}!{}",
337 outer_locator(graft.outer),
338 graft.inner.locator(inner)
339 )
340 }
341 }
342 }
343
344 fn graft_fill(&self, outer_leaf: NodeId, inner_path: String) -> quarb::Provenance {
350 let mut fill = self.outer.provenance(outer_leaf).as_source_rung();
351 fill.path = Some(inner_path);
352 fill
353 }
354
355 fn split(&self, node: NodeId) -> Option<(usize, NodeId)> {
356 if node.0 & GRAFT_BIT == 0 {
357 return None;
358 }
359 self.reverse
360 .borrow()
361 .get((node.0 & !GRAFT_BIT) as usize)
362 .copied()
363 }
364
365 fn intern(&self, graft: usize, inner: NodeId) -> NodeId {
366 if let Some(&id) = self.interned.borrow().get(&(graft, inner)) {
367 return id;
368 }
369 let mut rev = self.reverse.borrow_mut();
370 let id = NodeId(GRAFT_BIT | rev.len() as u64);
371 rev.push((graft, inner));
372 self.interned.borrow_mut().insert((graft, inner), id);
373 id
374 }
375
376 fn graft_at(&self, node: NodeId) -> Option<usize> {
380 if let Some(&g) = self.probed.borrow().get(&node) {
381 return g;
382 }
383 let g = (|| {
384 if !self.outer.children(node).is_empty() {
385 return None;
386 }
387 let name = self.outer.name(node)?;
388 if archive_name(&name)
389 && let Some(path_of) = self.source_path
390 && let Some(path) = path_of(&self.outer, node)
391 && let Ok(a) = quarb_archive::ArchiveAdapter::open(&path)
392 {
393 let inner = Inner::Archive(Box::new(
394 ComposeAdapter::new(a)
395 .with_source_graft(self.source_graft)
396 .with_document_graft(self.document_graft),
397 ));
398 let mut grafts = self.grafts.borrow_mut();
399 grafts.push(Graft { outer: node, inner });
400 return Some(grafts.len() - 1);
401 }
402 if let Some(read) = &self.document_reader
403 && let Some(path_of) = self.source_path
404 && let Some(path) = path_of(&self.outer, node)
405 && let Some(model) = read(&path)
406 {
407 let mut grafts = self.grafts.borrow_mut();
408 grafts.push(Graft {
409 outer: node,
410 inner: Inner::Text(model),
411 });
412 return Some(grafts.len() - 1);
413 }
414 let content = match self.outer.default_value(node)? {
415 Value::Str(s) => s,
416 _ => return None,
417 };
418 let mut inner = parse_inner(&name, &content, self.source_graft, self.document_graft)?;
419 if let (Inner::Text(model), Some(f)) = (&mut inner, self.document_url)
420 && let Some(u) = f(&self.outer, node)
421 {
422 model.set_document_url(&u);
423 }
424 let mut grafts = self.grafts.borrow_mut();
425 grafts.push(Graft { outer: node, inner });
426 Some(grafts.len() - 1)
427 })();
428 self.probed.borrow_mut().insert(node, g);
429 g
430 }
431
432 fn wrap(&self, graft: usize, inner: NodeId) -> NodeId {
435 let grafts = self.grafts.borrow();
436 if inner == grafts[graft].inner.adapter().root() {
437 grafts[graft].outer
438 } else {
439 drop(grafts);
440 self.intern(graft, inner)
441 }
442 }
443}
444
445impl<A: AstAdapter> AstAdapter for ComposeAdapter<A> {
446 fn root(&self) -> NodeId {
447 self.outer.root()
448 }
449
450 fn children(&self, node: NodeId) -> Vec<NodeId> {
451 match self.split(node) {
452 Some((g, inner)) => {
453 let ids: Vec<NodeId> = {
454 let grafts = self.grafts.borrow();
455 grafts[g].inner.adapter().children(inner)
456 };
457 ids.into_iter().map(|c| self.wrap(g, c)).collect()
458 }
459 None => {
460 let outer = self.outer.children(node);
461 if !outer.is_empty() {
462 return outer;
463 }
464 match self.graft_at(node) {
465 Some(g) => {
466 let ids: Vec<NodeId> = {
467 let grafts = self.grafts.borrow();
468 let a = grafts[g].inner.adapter();
469 a.children(a.root())
470 };
471 ids.into_iter().map(|c| self.wrap(g, c)).collect()
472 }
473 None => Vec::new(),
474 }
475 }
476 }
477 }
478
479 fn name(&self, node: NodeId) -> Option<String> {
480 match self.split(node) {
481 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().name(inner),
482 None => self.outer.name(node),
483 }
484 }
485
486 fn parent(&self, node: NodeId) -> Option<NodeId> {
487 match self.split(node) {
488 Some((g, inner)) => {
489 let p = self.grafts.borrow()[g].inner.adapter().parent(inner)?;
490 Some(self.wrap(g, p))
491 }
492 None => self.outer.parent(node),
493 }
494 }
495
496 fn child_host(&self, node: NodeId) -> NodeId {
497 match self.split(node) {
498 Some((g, inner)) => {
499 let h = self.grafts.borrow()[g].inner.adapter().child_host(inner);
500 self.wrap(g, h)
501 }
502 None => self.outer.child_host(node),
503 }
504 }
505
506 fn properties(&self, node: NodeId) -> Vec<(String, Value)> {
507 match self.split(node) {
508 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().properties(inner),
509 None => self.outer.properties(node),
510 }
511 }
512
513 fn non_tree_nodes(&self) -> Vec<NodeId> {
516 let mut out = self.outer.non_tree_nodes();
517 let grafts = self.grafts.borrow();
518 for (g, graft) in grafts.iter().enumerate() {
519 out.extend(
520 graft
521 .inner
522 .adapter()
523 .non_tree_nodes()
524 .into_iter()
525 .map(|n| self.wrap(g, n)),
526 );
527 }
528 out
529 }
530
531 fn answers_to(&self, node: NodeId, name: &str) -> bool {
534 match self.split(node) {
535 Some((g, inner)) => self.grafts.borrow()[g]
536 .inner
537 .adapter()
538 .answers_to(inner, name),
539 None => self.outer.answers_to(node, name),
540 }
541 }
542
543 fn traits(&self, node: NodeId) -> Vec<String> {
547 match self.split(node) {
548 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().traits(inner),
549 None => {
550 let mut out = self.outer.traits(node);
551 if let Some(g) = self.graft_at(node) {
552 let grafts = self.grafts.borrow();
553 let a = grafts[g].inner.adapter();
554 for t in a.traits(a.root()) {
555 if !out.contains(&t) {
556 out.push(t);
557 }
558 }
559 }
560 out
561 }
562 }
563 }
564
565 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
566 match self.split(node) {
567 Some((g, inner)) => self.grafts.borrow()[g]
568 .inner
569 .adapter()
570 .property(inner, name),
571 None => self.outer.property(node, name).or_else(|| {
576 let g = self.graft_at(node)?;
577 let grafts = self.grafts.borrow();
578 let a = grafts[g].inner.adapter();
579 a.property(a.root(), name)
580 }),
581 }
582 }
583
584 fn default_value(&self, node: NodeId) -> Option<Value> {
585 match self.split(node) {
586 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().default_value(inner),
587 None => self.outer.default_value(node),
588 }
589 }
590
591 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
592 match self.split(node) {
593 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().metadata(inner, key),
594 None => self.outer.metadata(node, key).or_else(|| {
598 let g = self.graft_at(node)?;
599 let grafts = self.grafts.borrow();
600 let a = grafts[g].inner.adapter();
601 a.metadata(a.root(), key)
602 }),
603 }
604 }
605
606 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
610 match self.split(node) {
611 Some((g, inner)) => self.grafts.borrow()[g]
612 .inner
613 .adapter()
614 .aliased_metadata(inner),
615 None => self.outer.aliased_metadata(node),
616 }
617 }
618
619 fn provenance(&self, node: NodeId) -> quarb::Provenance {
626 match self.split(node) {
627 Some((g, inner)) => {
628 let (inner_prov, outer_leaf, inner_path) = {
632 let grafts = self.grafts.borrow();
633 let a = grafts[g].inner.adapter();
634 (
635 a.provenance(inner),
636 grafts[g].outer,
637 quarb::name_path(a, inner),
638 )
639 };
640 inner_prov.or(self.graft_fill(outer_leaf, inner_path))
641 }
642 None => self.outer.provenance(node),
643 }
644 }
645 fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
647 match self.split(node) {
648 Some((g, inner)) => {
649 let (inner_list, outer_leaf, inner_path) = {
650 let grafts = self.grafts.borrow();
651 let a = grafts[g].inner.adapter();
652 (
653 a.provenance_list(inner),
654 grafts[g].outer,
655 quarb::name_path(a, inner),
656 )
657 };
658 inner_list.or(self.graft_fill(outer_leaf, inner_path))
659 }
660 None => self.outer.provenance_list(node),
661 }
662 }
663
664 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
665 match self.split(node) {
666 Some((g, inner)) => {
667 let t = self.grafts.borrow()[g]
668 .inner
669 .adapter()
670 .resolve(inner, property, hint)?;
671 Some(self.wrap(g, t))
672 }
673 None => self.outer.resolve(node, property, hint),
674 }
675 }
676
677 fn has_trait(&self, node: NodeId, name: &str) -> bool {
680 match self.split(node) {
681 Some((g, inner)) => self.grafts.borrow()[g]
682 .inner
683 .adapter()
684 .has_trait(inner, name),
685 None => {
686 self.outer.has_trait(node, name)
687 || self.graft_at(node).is_some_and(|g| {
688 let grafts = self.grafts.borrow();
689 let a = grafts[g].inner.adapter();
690 a.has_trait(a.root(), name)
691 })
692 }
693 }
694 }
695
696 fn ref_property(&self, node: NodeId) -> Option<String> {
697 match self.split(node) {
698 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().ref_property(inner),
699 None => self.outer.ref_property(node),
700 }
701 }
702
703 fn external_ref(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<String> {
706 match self.split(node) {
707 Some((g, inner)) => self.grafts.borrow()[g]
708 .inner
709 .adapter()
710 .external_ref(inner, property, hint),
711 None => self.outer.external_ref(node, property, hint),
712 }
713 }
714
715 fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
716 match self.split(node) {
717 Some((g, inner)) => self.grafts.borrow()[g]
718 .inner
719 .adapter()
720 .ref_label(inner, property),
721 None => self.outer.ref_label(node, property),
722 }
723 }
724
725 fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
728 match self.split(node) {
729 Some((g, inner)) => {
730 let t = self.grafts.borrow()[g]
731 .inner
732 .adapter()
733 .resolve_fragment(inner, fragment)?;
734 Some(self.wrap(g, t))
735 }
736 None => match self.graft_at(node) {
737 Some(g) => {
738 let t = {
739 let grafts = self.grafts.borrow();
740 let a = grafts[g].inner.adapter();
741 a.resolve_fragment(a.root(), fragment)?
742 };
743 Some(self.wrap(g, t))
744 }
745 None => self.outer.resolve_fragment(node, fragment),
746 },
747 }
748 }
749
750 fn document_by_ref(&self, id: &str) -> Option<NodeId> {
753 if let Some(leaf) = self.leaf_by_url(id) {
754 let _ = self.graft_at(leaf);
756 return Some(leaf);
757 }
758 self.outer.document_by_ref(id)
759 }
760
761 fn reverse_resolve(
765 &self,
766 node: NodeId,
767 property: Option<&str>,
768 hint: Option<&str>,
769 ) -> Option<Vec<NodeId>> {
770 match self.split(node) {
771 Some((g, inner)) => {
772 let v = self.grafts.borrow()[g]
773 .inner
774 .adapter()
775 .reverse_resolve(inner, property, hint)?;
776 Some(v.into_iter().map(|t| self.wrap(g, t)).collect())
777 }
778 None => None,
779 }
780 }
781
782 fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
785 let (g, inner) = self.split(node)?;
786 let v = self.grafts.borrow()[g]
787 .inner
788 .adapter()
789 .descendants_named(inner, name)?;
790 Some(v.into_iter().map(|(t, d)| (self.wrap(g, t), d)).collect())
791 }
792
793 fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
794 let outer: Vec<NodeId> = nodes
795 .iter()
796 .copied()
797 .filter(|n| self.split(*n).is_none())
798 .collect();
799 if !outer.is_empty() {
800 self.outer.prefetch_links(&outer, dir);
801 }
802 }
803
804 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
805 match self.split(node) {
806 Some((g, inner)) => {
807 let ls: Vec<(String, NodeId)> = {
808 let grafts = self.grafts.borrow();
809 grafts[g].inner.adapter().links(inner)
810 };
811 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
812 }
813 None => self.outer.links(node),
814 }
815 }
816
817 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
818 match self.split(node) {
819 Some((g, inner)) => {
820 let ls: Vec<(String, NodeId)> = {
821 let grafts = self.grafts.borrow();
822 grafts[g].inner.adapter().backlinks(inner)
823 };
824 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
825 }
826 None => self.outer.backlinks(node),
827 }
828 }
829
830 fn link_property(
831 &self,
832 source: NodeId,
833 label: &str,
834 target: NodeId,
835 name: &str,
836 ) -> Option<Value> {
837 match (self.split(source), self.split(target)) {
840 (Some((g, src)), Some((gt, tgt))) if g == gt => self.grafts.borrow()[g]
841 .inner
842 .adapter()
843 .link_property(src, label, tgt, name),
844 (None, None) => self.outer.link_property(source, label, target, name),
845 _ => None,
846 }
847 }
848}