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()).or_insert(n);
267 }
268 } else {
269 stack.extend(kids);
270 }
271 }
272 *self.urls.borrow_mut() = Some(map);
273 }
274 self.urls.borrow().as_ref()?.get(url).copied()
275 }
276
277 pub fn with_source_graft(mut self, graft: SourceGraft) -> Self {
280 self.source_graft = graft;
281 self
282 }
283
284 pub fn with_document_graft(mut self, graft: DocumentGraft) -> Self {
287 self.document_graft = graft;
288 self
289 }
290
291 pub fn with_source_paths(outer: A, source_path: fn(&A, NodeId) -> Option<PathBuf>) -> Self {
297 ComposeAdapter {
298 source_path: Some(source_path),
299 ..Self::new(outer)
300 }
301 }
302
303 pub fn outer(&self) -> &A {
305 &self.outer
306 }
307
308 pub fn locator(&self, node: NodeId, outer_locator: impl Fn(NodeId) -> String) -> String {
311 match self.split(node) {
312 None => outer_locator(node),
313 Some((g, inner)) => {
314 let grafts = self.grafts.borrow();
315 let graft = &grafts[g];
316 format!(
317 "{}!{}",
318 outer_locator(graft.outer),
319 graft.inner.locator(inner)
320 )
321 }
322 }
323 }
324
325 fn graft_fill(&self, outer_leaf: NodeId, inner_path: String) -> quarb::Provenance {
331 let mut fill = self.outer.provenance(outer_leaf).as_source_rung();
332 fill.path = Some(inner_path);
333 fill
334 }
335
336 fn split(&self, node: NodeId) -> Option<(usize, NodeId)> {
337 if node.0 & GRAFT_BIT == 0 {
338 return None;
339 }
340 self.reverse
341 .borrow()
342 .get((node.0 & !GRAFT_BIT) as usize)
343 .copied()
344 }
345
346 fn intern(&self, graft: usize, inner: NodeId) -> NodeId {
347 if let Some(&id) = self.interned.borrow().get(&(graft, inner)) {
348 return id;
349 }
350 let mut rev = self.reverse.borrow_mut();
351 let id = NodeId(GRAFT_BIT | rev.len() as u64);
352 rev.push((graft, inner));
353 self.interned.borrow_mut().insert((graft, inner), id);
354 id
355 }
356
357 fn graft_at(&self, node: NodeId) -> Option<usize> {
361 if let Some(&g) = self.probed.borrow().get(&node) {
362 return g;
363 }
364 let g = (|| {
365 if !self.outer.children(node).is_empty() {
366 return None;
367 }
368 let name = self.outer.name(node)?;
369 if archive_name(&name)
370 && let Some(path_of) = self.source_path
371 && let Some(path) = path_of(&self.outer, node)
372 && let Ok(a) = quarb_archive::ArchiveAdapter::open(&path)
373 {
374 let inner = Inner::Archive(Box::new(
375 ComposeAdapter::new(a)
376 .with_source_graft(self.source_graft)
377 .with_document_graft(self.document_graft),
378 ));
379 let mut grafts = self.grafts.borrow_mut();
380 grafts.push(Graft { outer: node, inner });
381 return Some(grafts.len() - 1);
382 }
383 let content = match self.outer.default_value(node)? {
384 Value::Str(s) => s,
385 _ => return None,
386 };
387 let mut inner = parse_inner(&name, &content, self.source_graft, self.document_graft)?;
388 if let (Inner::Text(model), Some(f)) = (&mut inner, self.document_url)
389 && let Some(u) = f(&self.outer, node)
390 {
391 model.set_document_url(&u);
392 }
393 let mut grafts = self.grafts.borrow_mut();
394 grafts.push(Graft { outer: node, inner });
395 Some(grafts.len() - 1)
396 })();
397 self.probed.borrow_mut().insert(node, g);
398 g
399 }
400
401 fn wrap(&self, graft: usize, inner: NodeId) -> NodeId {
404 let grafts = self.grafts.borrow();
405 if inner == grafts[graft].inner.adapter().root() {
406 grafts[graft].outer
407 } else {
408 drop(grafts);
409 self.intern(graft, inner)
410 }
411 }
412}
413
414impl<A: AstAdapter> AstAdapter for ComposeAdapter<A> {
415 fn root(&self) -> NodeId {
416 self.outer.root()
417 }
418
419 fn children(&self, node: NodeId) -> Vec<NodeId> {
420 match self.split(node) {
421 Some((g, inner)) => {
422 let ids: Vec<NodeId> = {
423 let grafts = self.grafts.borrow();
424 grafts[g].inner.adapter().children(inner)
425 };
426 ids.into_iter().map(|c| self.wrap(g, c)).collect()
427 }
428 None => {
429 let outer = self.outer.children(node);
430 if !outer.is_empty() {
431 return outer;
432 }
433 match self.graft_at(node) {
434 Some(g) => {
435 let ids: Vec<NodeId> = {
436 let grafts = self.grafts.borrow();
437 let a = grafts[g].inner.adapter();
438 a.children(a.root())
439 };
440 ids.into_iter().map(|c| self.wrap(g, c)).collect()
441 }
442 None => Vec::new(),
443 }
444 }
445 }
446 }
447
448 fn name(&self, node: NodeId) -> Option<String> {
449 match self.split(node) {
450 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().name(inner),
451 None => self.outer.name(node),
452 }
453 }
454
455 fn parent(&self, node: NodeId) -> Option<NodeId> {
456 match self.split(node) {
457 Some((g, inner)) => {
458 let p = self.grafts.borrow()[g].inner.adapter().parent(inner)?;
459 Some(self.wrap(g, p))
460 }
461 None => self.outer.parent(node),
462 }
463 }
464
465 fn answers_to(&self, node: NodeId, name: &str) -> bool {
468 match self.split(node) {
469 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().answers_to(inner, name),
470 None => self.outer.answers_to(node, name),
471 }
472 }
473
474 fn traits(&self, node: NodeId) -> Vec<String> {
478 match self.split(node) {
479 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().traits(inner),
480 None => {
481 let mut out = self.outer.traits(node);
482 if let Some(g) = self.graft_at(node) {
483 let grafts = self.grafts.borrow();
484 let a = grafts[g].inner.adapter();
485 for t in a.traits(a.root()) {
486 if !out.contains(&t) {
487 out.push(t);
488 }
489 }
490 }
491 out
492 }
493 }
494 }
495
496 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
497 match self.split(node) {
498 Some((g, inner)) => self.grafts.borrow()[g]
499 .inner
500 .adapter()
501 .property(inner, name),
502 None => self.outer.property(node, name).or_else(|| {
507 let g = self.graft_at(node)?;
508 let grafts = self.grafts.borrow();
509 let a = grafts[g].inner.adapter();
510 a.property(a.root(), name)
511 }),
512 }
513 }
514
515 fn default_value(&self, node: NodeId) -> Option<Value> {
516 match self.split(node) {
517 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().default_value(inner),
518 None => self.outer.default_value(node),
519 }
520 }
521
522 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
523 match self.split(node) {
524 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().metadata(inner, key),
525 None => self.outer.metadata(node, key).or_else(|| {
529 let g = self.graft_at(node)?;
530 let grafts = self.grafts.borrow();
531 let a = grafts[g].inner.adapter();
532 a.metadata(a.root(), key)
533 }),
534 }
535 }
536
537 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
541 match self.split(node) {
542 Some((g, inner)) => self.grafts.borrow()[g]
543 .inner
544 .adapter()
545 .aliased_metadata(inner),
546 None => self.outer.aliased_metadata(node),
547 }
548 }
549
550 fn provenance(&self, node: NodeId) -> quarb::Provenance {
557 match self.split(node) {
558 Some((g, inner)) => {
559 let (inner_prov, outer_leaf, inner_path) = {
563 let grafts = self.grafts.borrow();
564 let a = grafts[g].inner.adapter();
565 (a.provenance(inner), grafts[g].outer, quarb::name_path(a, inner))
566 };
567 inner_prov.or(self.graft_fill(outer_leaf, inner_path))
568 }
569 None => self.outer.provenance(node),
570 }
571 }
572 fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
574 match self.split(node) {
575 Some((g, inner)) => {
576 let (inner_list, outer_leaf, inner_path) = {
577 let grafts = self.grafts.borrow();
578 let a = grafts[g].inner.adapter();
579 (a.provenance_list(inner), grafts[g].outer, quarb::name_path(a, inner))
580 };
581 inner_list.or(self.graft_fill(outer_leaf, inner_path))
582 }
583 None => self.outer.provenance_list(node),
584 }
585 }
586
587 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
588 match self.split(node) {
589 Some((g, inner)) => {
590 let t = self.grafts.borrow()[g]
591 .inner
592 .adapter()
593 .resolve(inner, property, hint)?;
594 Some(self.wrap(g, t))
595 }
596 None => self.outer.resolve(node, property, hint),
597 }
598 }
599
600 fn has_trait(&self, node: NodeId, name: &str) -> bool {
603 match self.split(node) {
604 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().has_trait(inner, name),
605 None => {
606 self.outer.has_trait(node, name)
607 || self.graft_at(node).is_some_and(|g| {
608 let grafts = self.grafts.borrow();
609 let a = grafts[g].inner.adapter();
610 a.has_trait(a.root(), name)
611 })
612 }
613 }
614 }
615
616 fn ref_property(&self, node: NodeId) -> Option<String> {
617 match self.split(node) {
618 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().ref_property(inner),
619 None => self.outer.ref_property(node),
620 }
621 }
622
623 fn external_ref(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<String> {
626 match self.split(node) {
627 Some((g, inner)) => self.grafts.borrow()[g]
628 .inner
629 .adapter()
630 .external_ref(inner, property, hint),
631 None => self.outer.external_ref(node, property, hint),
632 }
633 }
634
635 fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
636 match self.split(node) {
637 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().ref_label(inner, property),
638 None => self.outer.ref_label(node, property),
639 }
640 }
641
642 fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
645 match self.split(node) {
646 Some((g, inner)) => {
647 let t = self.grafts.borrow()[g].inner.adapter().resolve_fragment(inner, fragment)?;
648 Some(self.wrap(g, t))
649 }
650 None => match self.graft_at(node) {
651 Some(g) => {
652 let t = {
653 let grafts = self.grafts.borrow();
654 let a = grafts[g].inner.adapter();
655 a.resolve_fragment(a.root(), fragment)?
656 };
657 Some(self.wrap(g, t))
658 }
659 None => self.outer.resolve_fragment(node, fragment),
660 },
661 }
662 }
663
664 fn document_by_ref(&self, id: &str) -> Option<NodeId> {
667 if let Some(leaf) = self.leaf_by_url(id) {
668 let _ = self.graft_at(leaf);
670 return Some(leaf);
671 }
672 self.outer.document_by_ref(id)
673 }
674
675 fn reverse_resolve(&self, node: NodeId, property: Option<&str>, hint: Option<&str>) -> Option<Vec<NodeId>> {
679 match self.split(node) {
680 Some((g, inner)) => {
681 let v = self.grafts.borrow()[g].inner.adapter().reverse_resolve(inner, property, hint)?;
682 Some(v.into_iter().map(|t| self.wrap(g, t)).collect())
683 }
684 None => None,
685 }
686 }
687
688 fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
691 let (g, inner) = self.split(node)?;
692 let v = self.grafts.borrow()[g].inner.adapter().descendants_named(inner, name)?;
693 Some(v.into_iter().map(|(t, d)| (self.wrap(g, t), d)).collect())
694 }
695
696 fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
697 let outer: Vec<NodeId> = nodes.iter().copied().filter(|n| self.split(*n).is_none()).collect();
698 if !outer.is_empty() {
699 self.outer.prefetch_links(&outer, dir);
700 }
701 }
702
703 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
704 match self.split(node) {
705 Some((g, inner)) => {
706 let ls: Vec<(String, NodeId)> = {
707 let grafts = self.grafts.borrow();
708 grafts[g].inner.adapter().links(inner)
709 };
710 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
711 }
712 None => self.outer.links(node),
713 }
714 }
715
716 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
717 match self.split(node) {
718 Some((g, inner)) => {
719 let ls: Vec<(String, NodeId)> = {
720 let grafts = self.grafts.borrow();
721 grafts[g].inner.adapter().backlinks(inner)
722 };
723 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
724 }
725 None => self.outer.backlinks(node),
726 }
727 }
728
729 fn link_property(
730 &self,
731 source: NodeId,
732 label: &str,
733 target: NodeId,
734 name: &str,
735 ) -> Option<Value> {
736 match (self.split(source), self.split(target)) {
739 (Some((g, src)), Some((gt, tgt))) if g == gt => self.grafts.borrow()[g]
740 .inner
741 .adapter()
742 .link_property(src, label, tgt, name),
743 (None, None) => self.outer.link_property(source, label, target, name),
744 _ => None,
745 }
746 }
747}