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 answers_to(&self, node: NodeId, name: &str) -> bool {
499 match self.split(node) {
500 Some((g, inner)) => self.grafts.borrow()[g]
501 .inner
502 .adapter()
503 .answers_to(inner, name),
504 None => self.outer.answers_to(node, name),
505 }
506 }
507
508 fn traits(&self, node: NodeId) -> Vec<String> {
512 match self.split(node) {
513 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().traits(inner),
514 None => {
515 let mut out = self.outer.traits(node);
516 if let Some(g) = self.graft_at(node) {
517 let grafts = self.grafts.borrow();
518 let a = grafts[g].inner.adapter();
519 for t in a.traits(a.root()) {
520 if !out.contains(&t) {
521 out.push(t);
522 }
523 }
524 }
525 out
526 }
527 }
528 }
529
530 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
531 match self.split(node) {
532 Some((g, inner)) => self.grafts.borrow()[g]
533 .inner
534 .adapter()
535 .property(inner, name),
536 None => self.outer.property(node, name).or_else(|| {
541 let g = self.graft_at(node)?;
542 let grafts = self.grafts.borrow();
543 let a = grafts[g].inner.adapter();
544 a.property(a.root(), name)
545 }),
546 }
547 }
548
549 fn default_value(&self, node: NodeId) -> Option<Value> {
550 match self.split(node) {
551 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().default_value(inner),
552 None => self.outer.default_value(node),
553 }
554 }
555
556 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
557 match self.split(node) {
558 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().metadata(inner, key),
559 None => self.outer.metadata(node, key).or_else(|| {
563 let g = self.graft_at(node)?;
564 let grafts = self.grafts.borrow();
565 let a = grafts[g].inner.adapter();
566 a.metadata(a.root(), key)
567 }),
568 }
569 }
570
571 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
575 match self.split(node) {
576 Some((g, inner)) => self.grafts.borrow()[g]
577 .inner
578 .adapter()
579 .aliased_metadata(inner),
580 None => self.outer.aliased_metadata(node),
581 }
582 }
583
584 fn provenance(&self, node: NodeId) -> quarb::Provenance {
591 match self.split(node) {
592 Some((g, inner)) => {
593 let (inner_prov, outer_leaf, inner_path) = {
597 let grafts = self.grafts.borrow();
598 let a = grafts[g].inner.adapter();
599 (
600 a.provenance(inner),
601 grafts[g].outer,
602 quarb::name_path(a, inner),
603 )
604 };
605 inner_prov.or(self.graft_fill(outer_leaf, inner_path))
606 }
607 None => self.outer.provenance(node),
608 }
609 }
610 fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
612 match self.split(node) {
613 Some((g, inner)) => {
614 let (inner_list, outer_leaf, inner_path) = {
615 let grafts = self.grafts.borrow();
616 let a = grafts[g].inner.adapter();
617 (
618 a.provenance_list(inner),
619 grafts[g].outer,
620 quarb::name_path(a, inner),
621 )
622 };
623 inner_list.or(self.graft_fill(outer_leaf, inner_path))
624 }
625 None => self.outer.provenance_list(node),
626 }
627 }
628
629 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
630 match self.split(node) {
631 Some((g, inner)) => {
632 let t = self.grafts.borrow()[g]
633 .inner
634 .adapter()
635 .resolve(inner, property, hint)?;
636 Some(self.wrap(g, t))
637 }
638 None => self.outer.resolve(node, property, hint),
639 }
640 }
641
642 fn has_trait(&self, node: NodeId, name: &str) -> bool {
645 match self.split(node) {
646 Some((g, inner)) => self.grafts.borrow()[g]
647 .inner
648 .adapter()
649 .has_trait(inner, name),
650 None => {
651 self.outer.has_trait(node, name)
652 || self.graft_at(node).is_some_and(|g| {
653 let grafts = self.grafts.borrow();
654 let a = grafts[g].inner.adapter();
655 a.has_trait(a.root(), name)
656 })
657 }
658 }
659 }
660
661 fn ref_property(&self, node: NodeId) -> Option<String> {
662 match self.split(node) {
663 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().ref_property(inner),
664 None => self.outer.ref_property(node),
665 }
666 }
667
668 fn external_ref(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<String> {
671 match self.split(node) {
672 Some((g, inner)) => self.grafts.borrow()[g]
673 .inner
674 .adapter()
675 .external_ref(inner, property, hint),
676 None => self.outer.external_ref(node, property, hint),
677 }
678 }
679
680 fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
681 match self.split(node) {
682 Some((g, inner)) => self.grafts.borrow()[g]
683 .inner
684 .adapter()
685 .ref_label(inner, property),
686 None => self.outer.ref_label(node, property),
687 }
688 }
689
690 fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
693 match self.split(node) {
694 Some((g, inner)) => {
695 let t = self.grafts.borrow()[g]
696 .inner
697 .adapter()
698 .resolve_fragment(inner, fragment)?;
699 Some(self.wrap(g, t))
700 }
701 None => match self.graft_at(node) {
702 Some(g) => {
703 let t = {
704 let grafts = self.grafts.borrow();
705 let a = grafts[g].inner.adapter();
706 a.resolve_fragment(a.root(), fragment)?
707 };
708 Some(self.wrap(g, t))
709 }
710 None => self.outer.resolve_fragment(node, fragment),
711 },
712 }
713 }
714
715 fn document_by_ref(&self, id: &str) -> Option<NodeId> {
718 if let Some(leaf) = self.leaf_by_url(id) {
719 let _ = self.graft_at(leaf);
721 return Some(leaf);
722 }
723 self.outer.document_by_ref(id)
724 }
725
726 fn reverse_resolve(
730 &self,
731 node: NodeId,
732 property: Option<&str>,
733 hint: Option<&str>,
734 ) -> Option<Vec<NodeId>> {
735 match self.split(node) {
736 Some((g, inner)) => {
737 let v = self.grafts.borrow()[g]
738 .inner
739 .adapter()
740 .reverse_resolve(inner, property, hint)?;
741 Some(v.into_iter().map(|t| self.wrap(g, t)).collect())
742 }
743 None => None,
744 }
745 }
746
747 fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
750 let (g, inner) = self.split(node)?;
751 let v = self.grafts.borrow()[g]
752 .inner
753 .adapter()
754 .descendants_named(inner, name)?;
755 Some(v.into_iter().map(|(t, d)| (self.wrap(g, t), d)).collect())
756 }
757
758 fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
759 let outer: Vec<NodeId> = nodes
760 .iter()
761 .copied()
762 .filter(|n| self.split(*n).is_none())
763 .collect();
764 if !outer.is_empty() {
765 self.outer.prefetch_links(&outer, dir);
766 }
767 }
768
769 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
770 match self.split(node) {
771 Some((g, inner)) => {
772 let ls: Vec<(String, NodeId)> = {
773 let grafts = self.grafts.borrow();
774 grafts[g].inner.adapter().links(inner)
775 };
776 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
777 }
778 None => self.outer.links(node),
779 }
780 }
781
782 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
783 match self.split(node) {
784 Some((g, inner)) => {
785 let ls: Vec<(String, NodeId)> = {
786 let grafts = self.grafts.borrow();
787 grafts[g].inner.adapter().backlinks(inner)
788 };
789 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
790 }
791 None => self.outer.backlinks(node),
792 }
793 }
794
795 fn link_property(
796 &self,
797 source: NodeId,
798 label: &str,
799 target: NodeId,
800 name: &str,
801 ) -> Option<Value> {
802 match (self.split(source), self.split(target)) {
805 (Some((g, src)), Some((gt, tgt))) if g == gt => self.grafts.borrow()[g]
806 .inner
807 .adapter()
808 .link_property(src, label, tgt, name),
809 (None, None) => self.outer.link_property(source, label, target, name),
810 _ => None,
811 }
812 }
813}