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 struct ComposeAdapter<A: AstAdapter> {
205 outer: A,
206 grafts: RefCell<Vec<Graft>>,
207 probed: RefCell<HashMap<NodeId, Option<usize>>>,
210 interned: RefCell<HashMap<(usize, NodeId), NodeId>>,
212 reverse: RefCell<Vec<(usize, NodeId)>>,
213 source_path: Option<fn(&A, NodeId) -> Option<PathBuf>>,
217 source_graft: SourceGraft,
219 document_graft: DocumentGraft,
221 document_url: Option<fn(&A, NodeId) -> Option<String>>,
225 urls: RefCell<Option<HashMap<String, NodeId>>>,
227}
228
229impl<A: AstAdapter> ComposeAdapter<A> {
230 pub fn new(outer: A) -> Self {
231 ComposeAdapter {
232 outer,
233 grafts: RefCell::new(Vec::new()),
234 probed: RefCell::new(HashMap::new()),
235 interned: RefCell::new(HashMap::new()),
236 reverse: RefCell::new(Vec::new()),
237 source_path: None,
238 source_graft: SourceGraft::default(),
239 document_graft: DocumentGraft::default(),
240 document_url: None,
241 urls: RefCell::new(None),
242 }
243 }
244
245 pub fn with_document_urls(mut self, document_url: fn(&A, NodeId) -> Option<String>) -> Self {
251 self.document_url = Some(document_url);
252 self
253 }
254
255 fn leaf_by_url(&self, url: &str) -> Option<NodeId> {
258 let f = self.document_url?;
259 if self.urls.borrow().is_none() {
260 let mut map = HashMap::new();
261 let mut stack = vec![self.outer.root()];
262 while let Some(n) = stack.pop() {
263 let kids = self.outer.children(n);
264 if kids.is_empty() {
265 if let Some(u) = f(&self.outer, n) {
266 map.entry(u.split('#').next().unwrap_or(&u).to_string())
267 .or_insert(n);
268 }
269 } else {
270 stack.extend(kids);
271 }
272 }
273 *self.urls.borrow_mut() = Some(map);
274 }
275 self.urls.borrow().as_ref()?.get(url).copied()
276 }
277
278 pub fn with_source_graft(mut self, graft: SourceGraft) -> Self {
281 self.source_graft = graft;
282 self
283 }
284
285 pub fn with_document_graft(mut self, graft: DocumentGraft) -> Self {
288 self.document_graft = graft;
289 self
290 }
291
292 pub fn with_source_paths(outer: A, source_path: fn(&A, NodeId) -> Option<PathBuf>) -> Self {
298 ComposeAdapter {
299 source_path: Some(source_path),
300 ..Self::new(outer)
301 }
302 }
303
304 pub fn outer(&self) -> &A {
306 &self.outer
307 }
308
309 pub fn locator(&self, node: NodeId, outer_locator: impl Fn(NodeId) -> String) -> String {
312 match self.split(node) {
313 None => outer_locator(node),
314 Some((g, inner)) => {
315 let grafts = self.grafts.borrow();
316 let graft = &grafts[g];
317 format!(
318 "{}!{}",
319 outer_locator(graft.outer),
320 graft.inner.locator(inner)
321 )
322 }
323 }
324 }
325
326 fn graft_fill(&self, outer_leaf: NodeId, inner_path: String) -> quarb::Provenance {
332 let mut fill = self.outer.provenance(outer_leaf).as_source_rung();
333 fill.path = Some(inner_path);
334 fill
335 }
336
337 fn split(&self, node: NodeId) -> Option<(usize, NodeId)> {
338 if node.0 & GRAFT_BIT == 0 {
339 return None;
340 }
341 self.reverse
342 .borrow()
343 .get((node.0 & !GRAFT_BIT) as usize)
344 .copied()
345 }
346
347 fn intern(&self, graft: usize, inner: NodeId) -> NodeId {
348 if let Some(&id) = self.interned.borrow().get(&(graft, inner)) {
349 return id;
350 }
351 let mut rev = self.reverse.borrow_mut();
352 let id = NodeId(GRAFT_BIT | rev.len() as u64);
353 rev.push((graft, inner));
354 self.interned.borrow_mut().insert((graft, inner), id);
355 id
356 }
357
358 fn graft_at(&self, node: NodeId) -> Option<usize> {
362 if let Some(&g) = self.probed.borrow().get(&node) {
363 return g;
364 }
365 let g = (|| {
366 if !self.outer.children(node).is_empty() {
367 return None;
368 }
369 let name = self.outer.name(node)?;
370 if archive_name(&name)
371 && let Some(path_of) = self.source_path
372 && let Some(path) = path_of(&self.outer, node)
373 && let Ok(a) = quarb_archive::ArchiveAdapter::open(&path)
374 {
375 let inner = Inner::Archive(Box::new(
376 ComposeAdapter::new(a)
377 .with_source_graft(self.source_graft)
378 .with_document_graft(self.document_graft),
379 ));
380 let mut grafts = self.grafts.borrow_mut();
381 grafts.push(Graft { outer: node, inner });
382 return Some(grafts.len() - 1);
383 }
384 let content = match self.outer.default_value(node)? {
385 Value::Str(s) => s,
386 _ => return None,
387 };
388 let mut inner = parse_inner(&name, &content, self.source_graft, self.document_graft)?;
389 if let (Inner::Text(model), Some(f)) = (&mut inner, self.document_url)
390 && let Some(u) = f(&self.outer, node)
391 {
392 model.set_document_url(&u);
393 }
394 let mut grafts = self.grafts.borrow_mut();
395 grafts.push(Graft { outer: node, inner });
396 Some(grafts.len() - 1)
397 })();
398 self.probed.borrow_mut().insert(node, g);
399 g
400 }
401
402 fn wrap(&self, graft: usize, inner: NodeId) -> NodeId {
405 let grafts = self.grafts.borrow();
406 if inner == grafts[graft].inner.adapter().root() {
407 grafts[graft].outer
408 } else {
409 drop(grafts);
410 self.intern(graft, inner)
411 }
412 }
413}
414
415impl<A: AstAdapter> AstAdapter for ComposeAdapter<A> {
416 fn root(&self) -> NodeId {
417 self.outer.root()
418 }
419
420 fn children(&self, node: NodeId) -> Vec<NodeId> {
421 match self.split(node) {
422 Some((g, inner)) => {
423 let ids: Vec<NodeId> = {
424 let grafts = self.grafts.borrow();
425 grafts[g].inner.adapter().children(inner)
426 };
427 ids.into_iter().map(|c| self.wrap(g, c)).collect()
428 }
429 None => {
430 let outer = self.outer.children(node);
431 if !outer.is_empty() {
432 return outer;
433 }
434 match self.graft_at(node) {
435 Some(g) => {
436 let ids: Vec<NodeId> = {
437 let grafts = self.grafts.borrow();
438 let a = grafts[g].inner.adapter();
439 a.children(a.root())
440 };
441 ids.into_iter().map(|c| self.wrap(g, c)).collect()
442 }
443 None => Vec::new(),
444 }
445 }
446 }
447 }
448
449 fn name(&self, node: NodeId) -> Option<String> {
450 match self.split(node) {
451 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().name(inner),
452 None => self.outer.name(node),
453 }
454 }
455
456 fn parent(&self, node: NodeId) -> Option<NodeId> {
457 match self.split(node) {
458 Some((g, inner)) => {
459 let p = self.grafts.borrow()[g].inner.adapter().parent(inner)?;
460 Some(self.wrap(g, p))
461 }
462 None => self.outer.parent(node),
463 }
464 }
465
466 fn answers_to(&self, node: NodeId, name: &str) -> bool {
469 match self.split(node) {
470 Some((g, inner)) => self.grafts.borrow()[g]
471 .inner
472 .adapter()
473 .answers_to(inner, name),
474 None => self.outer.answers_to(node, name),
475 }
476 }
477
478 fn traits(&self, node: NodeId) -> Vec<String> {
482 match self.split(node) {
483 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().traits(inner),
484 None => {
485 let mut out = self.outer.traits(node);
486 if let Some(g) = self.graft_at(node) {
487 let grafts = self.grafts.borrow();
488 let a = grafts[g].inner.adapter();
489 for t in a.traits(a.root()) {
490 if !out.contains(&t) {
491 out.push(t);
492 }
493 }
494 }
495 out
496 }
497 }
498 }
499
500 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
501 match self.split(node) {
502 Some((g, inner)) => self.grafts.borrow()[g]
503 .inner
504 .adapter()
505 .property(inner, name),
506 None => self.outer.property(node, name).or_else(|| {
511 let g = self.graft_at(node)?;
512 let grafts = self.grafts.borrow();
513 let a = grafts[g].inner.adapter();
514 a.property(a.root(), name)
515 }),
516 }
517 }
518
519 fn default_value(&self, node: NodeId) -> Option<Value> {
520 match self.split(node) {
521 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().default_value(inner),
522 None => self.outer.default_value(node),
523 }
524 }
525
526 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
527 match self.split(node) {
528 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().metadata(inner, key),
529 None => self.outer.metadata(node, key).or_else(|| {
533 let g = self.graft_at(node)?;
534 let grafts = self.grafts.borrow();
535 let a = grafts[g].inner.adapter();
536 a.metadata(a.root(), key)
537 }),
538 }
539 }
540
541 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
545 match self.split(node) {
546 Some((g, inner)) => self.grafts.borrow()[g]
547 .inner
548 .adapter()
549 .aliased_metadata(inner),
550 None => self.outer.aliased_metadata(node),
551 }
552 }
553
554 fn provenance(&self, node: NodeId) -> quarb::Provenance {
561 match self.split(node) {
562 Some((g, inner)) => {
563 let (inner_prov, outer_leaf, inner_path) = {
567 let grafts = self.grafts.borrow();
568 let a = grafts[g].inner.adapter();
569 (
570 a.provenance(inner),
571 grafts[g].outer,
572 quarb::name_path(a, inner),
573 )
574 };
575 inner_prov.or(self.graft_fill(outer_leaf, inner_path))
576 }
577 None => self.outer.provenance(node),
578 }
579 }
580 fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
582 match self.split(node) {
583 Some((g, inner)) => {
584 let (inner_list, outer_leaf, inner_path) = {
585 let grafts = self.grafts.borrow();
586 let a = grafts[g].inner.adapter();
587 (
588 a.provenance_list(inner),
589 grafts[g].outer,
590 quarb::name_path(a, inner),
591 )
592 };
593 inner_list.or(self.graft_fill(outer_leaf, inner_path))
594 }
595 None => self.outer.provenance_list(node),
596 }
597 }
598
599 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
600 match self.split(node) {
601 Some((g, inner)) => {
602 let t = self.grafts.borrow()[g]
603 .inner
604 .adapter()
605 .resolve(inner, property, hint)?;
606 Some(self.wrap(g, t))
607 }
608 None => self.outer.resolve(node, property, hint),
609 }
610 }
611
612 fn has_trait(&self, node: NodeId, name: &str) -> bool {
615 match self.split(node) {
616 Some((g, inner)) => self.grafts.borrow()[g]
617 .inner
618 .adapter()
619 .has_trait(inner, name),
620 None => {
621 self.outer.has_trait(node, name)
622 || self.graft_at(node).is_some_and(|g| {
623 let grafts = self.grafts.borrow();
624 let a = grafts[g].inner.adapter();
625 a.has_trait(a.root(), name)
626 })
627 }
628 }
629 }
630
631 fn ref_property(&self, node: NodeId) -> Option<String> {
632 match self.split(node) {
633 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().ref_property(inner),
634 None => self.outer.ref_property(node),
635 }
636 }
637
638 fn external_ref(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<String> {
641 match self.split(node) {
642 Some((g, inner)) => self.grafts.borrow()[g]
643 .inner
644 .adapter()
645 .external_ref(inner, property, hint),
646 None => self.outer.external_ref(node, property, hint),
647 }
648 }
649
650 fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
651 match self.split(node) {
652 Some((g, inner)) => self.grafts.borrow()[g]
653 .inner
654 .adapter()
655 .ref_label(inner, property),
656 None => self.outer.ref_label(node, property),
657 }
658 }
659
660 fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
663 match self.split(node) {
664 Some((g, inner)) => {
665 let t = self.grafts.borrow()[g]
666 .inner
667 .adapter()
668 .resolve_fragment(inner, fragment)?;
669 Some(self.wrap(g, t))
670 }
671 None => match self.graft_at(node) {
672 Some(g) => {
673 let t = {
674 let grafts = self.grafts.borrow();
675 let a = grafts[g].inner.adapter();
676 a.resolve_fragment(a.root(), fragment)?
677 };
678 Some(self.wrap(g, t))
679 }
680 None => self.outer.resolve_fragment(node, fragment),
681 },
682 }
683 }
684
685 fn document_by_ref(&self, id: &str) -> Option<NodeId> {
688 if let Some(leaf) = self.leaf_by_url(id) {
689 let _ = self.graft_at(leaf);
691 return Some(leaf);
692 }
693 self.outer.document_by_ref(id)
694 }
695
696 fn reverse_resolve(
700 &self,
701 node: NodeId,
702 property: Option<&str>,
703 hint: Option<&str>,
704 ) -> Option<Vec<NodeId>> {
705 match self.split(node) {
706 Some((g, inner)) => {
707 let v = self.grafts.borrow()[g]
708 .inner
709 .adapter()
710 .reverse_resolve(inner, property, hint)?;
711 Some(v.into_iter().map(|t| self.wrap(g, t)).collect())
712 }
713 None => None,
714 }
715 }
716
717 fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
720 let (g, inner) = self.split(node)?;
721 let v = self.grafts.borrow()[g]
722 .inner
723 .adapter()
724 .descendants_named(inner, name)?;
725 Some(v.into_iter().map(|(t, d)| (self.wrap(g, t), d)).collect())
726 }
727
728 fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
729 let outer: Vec<NodeId> = nodes
730 .iter()
731 .copied()
732 .filter(|n| self.split(*n).is_none())
733 .collect();
734 if !outer.is_empty() {
735 self.outer.prefetch_links(&outer, dir);
736 }
737 }
738
739 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
740 match self.split(node) {
741 Some((g, inner)) => {
742 let ls: Vec<(String, NodeId)> = {
743 let grafts = self.grafts.borrow();
744 grafts[g].inner.adapter().links(inner)
745 };
746 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
747 }
748 None => self.outer.links(node),
749 }
750 }
751
752 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
753 match self.split(node) {
754 Some((g, inner)) => {
755 let ls: Vec<(String, NodeId)> = {
756 let grafts = self.grafts.borrow();
757 grafts[g].inner.adapter().backlinks(inner)
758 };
759 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
760 }
761 None => self.outer.backlinks(node),
762 }
763 }
764
765 fn link_property(
766 &self,
767 source: NodeId,
768 label: &str,
769 target: NodeId,
770 name: &str,
771 ) -> Option<Value> {
772 match (self.split(source), self.split(target)) {
775 (Some((g, src)), Some((gt, tgt))) if g == gt => self.grafts.borrow()[g]
776 .inner
777 .adapter()
778 .link_property(src, label, tgt, name),
779 (None, None) => self.outer.link_property(source, label, target, name),
780 _ => None,
781 }
782 }
783}