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 Syntax(quarb_tree_sitter::TreeSitterAdapter),
46 Code(quarb_code::CodeModel),
47 Archive(Box<ComposeAdapter<quarb_archive::ArchiveAdapter>>),
50}
51
52impl Inner {
53 fn adapter(&self) -> &dyn AstAdapter {
54 match self {
55 Inner::Json(a) => a,
56 Inner::Xml(a) => a,
57 Inner::Html(a) => a,
58 Inner::Text(a) => a,
59 Inner::Csv(a) => a,
60 Inner::Syntax(a) => a,
61 Inner::Code(a) => a,
62 Inner::Archive(a) => &**a,
63 }
64 }
65
66 fn locator(&self, node: NodeId) -> String {
67 match self {
68 Inner::Json(a) => a.pointer(node),
69 Inner::Xml(a) => a.locator(node),
70 Inner::Html(a) => a.locator(node),
71 Inner::Text(a) => a.locator(node),
72 Inner::Csv(a) => a.locator(node),
73 Inner::Syntax(a) => a.locator(node),
74 Inner::Code(a) => a.locator(node),
75 Inner::Archive(a) => a.locator(node, |o| a.outer().locator(o)),
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
87pub enum SourceGraft {
88 #[default]
89 Syntax,
90 Code,
91}
92
93fn archive_name(name: &str) -> bool {
97 let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
98 matches!(ext.as_str(), "zip" | "tar" | "tgz" | "gz")
99}
100
101fn parse_inner(name: &str, content: &str, graft: SourceGraft) -> Option<Inner> {
103 let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
104 match ext.as_str() {
105 "json" => {
106 return quarb_json::JsonAdapter::parse(content)
107 .ok()
108 .map(Inner::Json);
109 }
110 "jsonl" | "ndjson" => {
111 return quarb_json::JsonAdapter::parse_lines(content)
112 .ok()
113 .map(Inner::Json);
114 }
115 "xml" | "svg" | "xhtml" => {
116 return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
117 }
118 "html" | "htm" => return Some(Inner::Html(quarb_html::HtmlAdapter::parse(content))),
119 "yaml" | "yml" => return quarb_yaml::parse(content).ok().map(Inner::Json),
120 "toml" => return quarb_toml::parse(content).ok().map(Inner::Json),
121 "md" | "markdown" => return Some(Inner::Html(quarb_markdown::parse(content))),
122 "txt" => return Some(Inner::Text(quarb_text::TextModel::parse_plain(content))),
125 "csv" => return quarb_csv::CsvAdapter::parse(content).ok().map(Inner::Csv),
126 "tsv" => {
127 return quarb_csv::CsvAdapter::parse_with_delimiter(content, b'\t')
128 .ok()
129 .map(Inner::Csv);
130 }
131 ext if quarb_tree_sitter::supported(ext) => {
132 return match graft {
133 SourceGraft::Syntax => quarb_tree_sitter::TreeSitterAdapter::parse(content, ext)
134 .ok()
135 .map(Inner::Syntax),
136 SourceGraft::Code => {
137 quarb_code::CodeModel::parse(content, ext).ok().map(Inner::Code)
138 }
139 };
140 }
141 _ => {}
142 }
143 let t = content.trim_start();
145 if t.starts_with('{') || t.starts_with('[') {
146 return quarb_json::JsonAdapter::parse(content)
147 .ok()
148 .map(Inner::Json);
149 }
150 if t.starts_with("<?xml") {
151 return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
152 }
153 None
154}
155
156struct Graft {
158 outer: NodeId,
159 inner: Inner,
160}
161
162const GRAFT_BIT: u64 = 1 << 55;
171
172pub struct ComposeAdapter<A: AstAdapter> {
174 outer: A,
175 grafts: RefCell<Vec<Graft>>,
176 probed: RefCell<HashMap<NodeId, Option<usize>>>,
179 interned: RefCell<HashMap<(usize, NodeId), NodeId>>,
181 reverse: RefCell<Vec<(usize, NodeId)>>,
182 source_path: Option<fn(&A, NodeId) -> Option<PathBuf>>,
186 source_graft: SourceGraft,
188}
189
190impl<A: AstAdapter> ComposeAdapter<A> {
191 pub fn new(outer: A) -> Self {
192 ComposeAdapter {
193 outer,
194 grafts: RefCell::new(Vec::new()),
195 probed: RefCell::new(HashMap::new()),
196 interned: RefCell::new(HashMap::new()),
197 reverse: RefCell::new(Vec::new()),
198 source_path: None,
199 source_graft: SourceGraft::default(),
200 }
201 }
202
203 pub fn with_source_graft(mut self, graft: SourceGraft) -> Self {
206 self.source_graft = graft;
207 self
208 }
209
210 pub fn with_source_paths(outer: A, source_path: fn(&A, NodeId) -> Option<PathBuf>) -> Self {
216 ComposeAdapter {
217 source_path: Some(source_path),
218 ..Self::new(outer)
219 }
220 }
221
222 pub fn outer(&self) -> &A {
224 &self.outer
225 }
226
227 pub fn locator(&self, node: NodeId, outer_locator: impl Fn(NodeId) -> String) -> String {
230 match self.split(node) {
231 None => outer_locator(node),
232 Some((g, inner)) => {
233 let grafts = self.grafts.borrow();
234 let graft = &grafts[g];
235 format!(
236 "{}!{}",
237 outer_locator(graft.outer),
238 graft.inner.locator(inner)
239 )
240 }
241 }
242 }
243
244 fn split(&self, node: NodeId) -> Option<(usize, NodeId)> {
246 if node.0 & GRAFT_BIT == 0 {
247 return None;
248 }
249 self.reverse
250 .borrow()
251 .get((node.0 & !GRAFT_BIT) as usize)
252 .copied()
253 }
254
255 fn intern(&self, graft: usize, inner: NodeId) -> NodeId {
256 if let Some(&id) = self.interned.borrow().get(&(graft, inner)) {
257 return id;
258 }
259 let mut rev = self.reverse.borrow_mut();
260 let id = NodeId(GRAFT_BIT | rev.len() as u64);
261 rev.push((graft, inner));
262 self.interned.borrow_mut().insert((graft, inner), id);
263 id
264 }
265
266 fn graft_at(&self, node: NodeId) -> Option<usize> {
270 if let Some(&g) = self.probed.borrow().get(&node) {
271 return g;
272 }
273 let g = (|| {
274 if !self.outer.children(node).is_empty() {
275 return None;
276 }
277 let name = self.outer.name(node)?;
278 if archive_name(&name)
279 && let Some(path_of) = self.source_path
280 && let Some(path) = path_of(&self.outer, node)
281 && let Ok(a) = quarb_archive::ArchiveAdapter::open(&path)
282 {
283 let inner = Inner::Archive(Box::new(
284 ComposeAdapter::new(a).with_source_graft(self.source_graft),
285 ));
286 let mut grafts = self.grafts.borrow_mut();
287 grafts.push(Graft { outer: node, inner });
288 return Some(grafts.len() - 1);
289 }
290 let content = match self.outer.default_value(node)? {
291 Value::Str(s) => s,
292 _ => return None,
293 };
294 let inner = parse_inner(&name, &content, self.source_graft)?;
295 let mut grafts = self.grafts.borrow_mut();
296 grafts.push(Graft { outer: node, inner });
297 Some(grafts.len() - 1)
298 })();
299 self.probed.borrow_mut().insert(node, g);
300 g
301 }
302
303 fn wrap(&self, graft: usize, inner: NodeId) -> NodeId {
306 let grafts = self.grafts.borrow();
307 if inner == grafts[graft].inner.adapter().root() {
308 grafts[graft].outer
309 } else {
310 drop(grafts);
311 self.intern(graft, inner)
312 }
313 }
314}
315
316impl<A: AstAdapter> AstAdapter for ComposeAdapter<A> {
317 fn root(&self) -> NodeId {
318 self.outer.root()
319 }
320
321 fn children(&self, node: NodeId) -> Vec<NodeId> {
322 match self.split(node) {
323 Some((g, inner)) => {
324 let ids: Vec<NodeId> = {
325 let grafts = self.grafts.borrow();
326 grafts[g].inner.adapter().children(inner)
327 };
328 ids.into_iter().map(|c| self.wrap(g, c)).collect()
329 }
330 None => {
331 let outer = self.outer.children(node);
332 if !outer.is_empty() {
333 return outer;
334 }
335 match self.graft_at(node) {
336 Some(g) => {
337 let ids: Vec<NodeId> = {
338 let grafts = self.grafts.borrow();
339 let a = grafts[g].inner.adapter();
340 a.children(a.root())
341 };
342 ids.into_iter().map(|c| self.wrap(g, c)).collect()
343 }
344 None => Vec::new(),
345 }
346 }
347 }
348 }
349
350 fn name(&self, node: NodeId) -> Option<String> {
351 match self.split(node) {
352 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().name(inner),
353 None => self.outer.name(node),
354 }
355 }
356
357 fn parent(&self, node: NodeId) -> Option<NodeId> {
358 match self.split(node) {
359 Some((g, inner)) => {
360 let p = self.grafts.borrow()[g].inner.adapter().parent(inner)?;
361 Some(self.wrap(g, p))
362 }
363 None => self.outer.parent(node),
364 }
365 }
366
367 fn traits(&self, node: NodeId) -> Vec<String> {
368 match self.split(node) {
369 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().traits(inner),
370 None => self.outer.traits(node),
371 }
372 }
373
374 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
375 match self.split(node) {
376 Some((g, inner)) => self.grafts.borrow()[g]
377 .inner
378 .adapter()
379 .property(inner, name),
380 None => self.outer.property(node, name).or_else(|| {
385 let g = self.graft_at(node)?;
386 let grafts = self.grafts.borrow();
387 let a = grafts[g].inner.adapter();
388 a.property(a.root(), name)
389 }),
390 }
391 }
392
393 fn default_value(&self, node: NodeId) -> Option<Value> {
394 match self.split(node) {
395 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().default_value(inner),
396 None => self.outer.default_value(node),
397 }
398 }
399
400 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
401 match self.split(node) {
402 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().metadata(inner, key),
403 None => self.outer.metadata(node, key),
404 }
405 }
406
407 fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
411 match self.split(node) {
412 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().aliased_metadata(inner),
413 None => self.outer.aliased_metadata(node),
414 }
415 }
416
417 fn provenance(&self, node: NodeId) -> quarb::Provenance {
424 match self.split(node) {
425 Some((g, inner)) => {
426 let (inner_prov, outer_leaf) = {
430 let grafts = self.grafts.borrow();
431 (
432 grafts[g].inner.adapter().provenance(inner),
433 grafts[g].outer,
434 )
435 };
436 inner_prov.or(self.outer.provenance(outer_leaf))
437 }
438 None => self.outer.provenance(node),
439 }
440 }
441
442 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
443 match self.split(node) {
444 Some((g, inner)) => {
445 let t = self.grafts.borrow()[g]
446 .inner
447 .adapter()
448 .resolve(inner, property, hint)?;
449 Some(self.wrap(g, t))
450 }
451 None => self.outer.resolve(node, property, hint),
452 }
453 }
454
455 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
456 match self.split(node) {
457 Some((g, inner)) => {
458 let ls: Vec<(String, NodeId)> = {
459 let grafts = self.grafts.borrow();
460 grafts[g].inner.adapter().links(inner)
461 };
462 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
463 }
464 None => self.outer.links(node),
465 }
466 }
467
468 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
469 match self.split(node) {
470 Some((g, inner)) => {
471 let ls: Vec<(String, NodeId)> = {
472 let grafts = self.grafts.borrow();
473 grafts[g].inner.adapter().backlinks(inner)
474 };
475 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
476 }
477 None => self.outer.backlinks(node),
478 }
479 }
480
481 fn link_property(
482 &self,
483 source: NodeId,
484 label: &str,
485 target: NodeId,
486 name: &str,
487 ) -> Option<Value> {
488 match (self.split(source), self.split(target)) {
491 (Some((g, src)), Some((gt, tgt))) if g == gt => self.grafts.borrow()[g]
492 .inner
493 .adapter()
494 .link_property(src, label, tgt, name),
495 (None, None) => self.outer.link_property(source, label, target, name),
496 _ => None,
497 }
498 }
499}