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 Code(quarb_code::CodeAdapter),
46 Archive(Box<ComposeAdapter<quarb_archive::ArchiveAdapter>>),
49}
50
51impl Inner {
52 fn adapter(&self) -> &dyn AstAdapter {
53 match self {
54 Inner::Json(a) => a,
55 Inner::Xml(a) => a,
56 Inner::Html(a) => a,
57 Inner::Text(a) => a,
58 Inner::Csv(a) => a,
59 Inner::Code(a) => a,
60 Inner::Archive(a) => &**a,
61 }
62 }
63
64 fn locator(&self, node: NodeId) -> String {
65 match self {
66 Inner::Json(a) => a.pointer(node),
67 Inner::Xml(a) => a.locator(node),
68 Inner::Html(a) => a.locator(node),
69 Inner::Text(a) => a.locator(node),
70 Inner::Csv(a) => a.locator(node),
71 Inner::Code(a) => a.locator(node),
72 Inner::Archive(a) => a.locator(node, |o| a.outer().locator(o)),
73 }
74 }
75}
76
77fn archive_name(name: &str) -> bool {
81 let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
82 matches!(ext.as_str(), "zip" | "tar" | "tgz" | "gz")
83}
84
85fn parse_inner(name: &str, content: &str) -> Option<Inner> {
87 let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
88 match ext.as_str() {
89 "json" => {
90 return quarb_json::JsonAdapter::parse(content)
91 .ok()
92 .map(Inner::Json);
93 }
94 "jsonl" | "ndjson" => {
95 return quarb_json::JsonAdapter::parse_lines(content)
96 .ok()
97 .map(Inner::Json);
98 }
99 "xml" | "svg" | "xhtml" => {
100 return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
101 }
102 "html" | "htm" => return Some(Inner::Html(quarb_html::HtmlAdapter::parse(content))),
103 "yaml" | "yml" => return quarb_yaml::parse(content).ok().map(Inner::Json),
104 "toml" => return quarb_toml::parse(content).ok().map(Inner::Json),
105 "md" | "markdown" => return Some(Inner::Html(quarb_markdown::parse(content))),
106 "txt" => return Some(Inner::Text(quarb_text::TextModel::parse_plain(content))),
109 "csv" => return quarb_csv::CsvAdapter::parse(content).ok().map(Inner::Csv),
110 "tsv" => {
111 return quarb_csv::CsvAdapter::parse_with_delimiter(content, b'\t')
112 .ok()
113 .map(Inner::Csv);
114 }
115 ext if quarb_code::supported(ext) => {
116 return quarb_code::CodeAdapter::parse(content, ext)
117 .ok()
118 .map(Inner::Code);
119 }
120 _ => {}
121 }
122 let t = content.trim_start();
124 if t.starts_with('{') || t.starts_with('[') {
125 return quarb_json::JsonAdapter::parse(content)
126 .ok()
127 .map(Inner::Json);
128 }
129 if t.starts_with("<?xml") {
130 return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
131 }
132 None
133}
134
135struct Graft {
137 outer: NodeId,
138 inner: Inner,
139}
140
141const GRAFT_BIT: u64 = 1 << 55;
150
151pub struct ComposeAdapter<A: AstAdapter> {
153 outer: A,
154 grafts: RefCell<Vec<Graft>>,
155 probed: RefCell<HashMap<NodeId, Option<usize>>>,
158 interned: RefCell<HashMap<(usize, NodeId), NodeId>>,
160 reverse: RefCell<Vec<(usize, NodeId)>>,
161 source_path: Option<fn(&A, NodeId) -> Option<PathBuf>>,
165}
166
167impl<A: AstAdapter> ComposeAdapter<A> {
168 pub fn new(outer: A) -> Self {
169 ComposeAdapter {
170 outer,
171 grafts: RefCell::new(Vec::new()),
172 probed: RefCell::new(HashMap::new()),
173 interned: RefCell::new(HashMap::new()),
174 reverse: RefCell::new(Vec::new()),
175 source_path: None,
176 }
177 }
178
179 pub fn with_source_paths(outer: A, source_path: fn(&A, NodeId) -> Option<PathBuf>) -> Self {
185 ComposeAdapter {
186 source_path: Some(source_path),
187 ..Self::new(outer)
188 }
189 }
190
191 pub fn outer(&self) -> &A {
193 &self.outer
194 }
195
196 pub fn locator(&self, node: NodeId, outer_locator: impl Fn(NodeId) -> String) -> String {
199 match self.split(node) {
200 None => outer_locator(node),
201 Some((g, inner)) => {
202 let grafts = self.grafts.borrow();
203 let graft = &grafts[g];
204 format!(
205 "{}!{}",
206 outer_locator(graft.outer),
207 graft.inner.locator(inner)
208 )
209 }
210 }
211 }
212
213 fn split(&self, node: NodeId) -> Option<(usize, NodeId)> {
215 if node.0 & GRAFT_BIT == 0 {
216 return None;
217 }
218 self.reverse
219 .borrow()
220 .get((node.0 & !GRAFT_BIT) as usize)
221 .copied()
222 }
223
224 fn intern(&self, graft: usize, inner: NodeId) -> NodeId {
225 if let Some(&id) = self.interned.borrow().get(&(graft, inner)) {
226 return id;
227 }
228 let mut rev = self.reverse.borrow_mut();
229 let id = NodeId(GRAFT_BIT | rev.len() as u64);
230 rev.push((graft, inner));
231 self.interned.borrow_mut().insert((graft, inner), id);
232 id
233 }
234
235 fn graft_at(&self, node: NodeId) -> Option<usize> {
239 if let Some(&g) = self.probed.borrow().get(&node) {
240 return g;
241 }
242 let g = (|| {
243 if !self.outer.children(node).is_empty() {
244 return None;
245 }
246 let name = self.outer.name(node)?;
247 if archive_name(&name)
248 && let Some(path_of) = self.source_path
249 && let Some(path) = path_of(&self.outer, node)
250 && let Ok(a) = quarb_archive::ArchiveAdapter::open(&path)
251 {
252 let inner = Inner::Archive(Box::new(ComposeAdapter::new(a)));
253 let mut grafts = self.grafts.borrow_mut();
254 grafts.push(Graft { outer: node, inner });
255 return Some(grafts.len() - 1);
256 }
257 let content = match self.outer.default_value(node)? {
258 Value::Str(s) => s,
259 _ => return None,
260 };
261 let inner = parse_inner(&name, &content)?;
262 let mut grafts = self.grafts.borrow_mut();
263 grafts.push(Graft { outer: node, inner });
264 Some(grafts.len() - 1)
265 })();
266 self.probed.borrow_mut().insert(node, g);
267 g
268 }
269
270 fn wrap(&self, graft: usize, inner: NodeId) -> NodeId {
273 let grafts = self.grafts.borrow();
274 if inner == grafts[graft].inner.adapter().root() {
275 grafts[graft].outer
276 } else {
277 drop(grafts);
278 self.intern(graft, inner)
279 }
280 }
281}
282
283impl<A: AstAdapter> AstAdapter for ComposeAdapter<A> {
284 fn root(&self) -> NodeId {
285 self.outer.root()
286 }
287
288 fn children(&self, node: NodeId) -> Vec<NodeId> {
289 match self.split(node) {
290 Some((g, inner)) => {
291 let ids: Vec<NodeId> = {
292 let grafts = self.grafts.borrow();
293 grafts[g].inner.adapter().children(inner)
294 };
295 ids.into_iter().map(|c| self.wrap(g, c)).collect()
296 }
297 None => {
298 let outer = self.outer.children(node);
299 if !outer.is_empty() {
300 return outer;
301 }
302 match self.graft_at(node) {
303 Some(g) => {
304 let ids: Vec<NodeId> = {
305 let grafts = self.grafts.borrow();
306 let a = grafts[g].inner.adapter();
307 a.children(a.root())
308 };
309 ids.into_iter().map(|c| self.wrap(g, c)).collect()
310 }
311 None => Vec::new(),
312 }
313 }
314 }
315 }
316
317 fn name(&self, node: NodeId) -> Option<String> {
318 match self.split(node) {
319 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().name(inner),
320 None => self.outer.name(node),
321 }
322 }
323
324 fn parent(&self, node: NodeId) -> Option<NodeId> {
325 match self.split(node) {
326 Some((g, inner)) => {
327 let p = self.grafts.borrow()[g].inner.adapter().parent(inner)?;
328 Some(self.wrap(g, p))
329 }
330 None => self.outer.parent(node),
331 }
332 }
333
334 fn traits(&self, node: NodeId) -> Vec<String> {
335 match self.split(node) {
336 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().traits(inner),
337 None => self.outer.traits(node),
338 }
339 }
340
341 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
342 match self.split(node) {
343 Some((g, inner)) => self.grafts.borrow()[g]
344 .inner
345 .adapter()
346 .property(inner, name),
347 None => self.outer.property(node, name),
348 }
349 }
350
351 fn default_value(&self, node: NodeId) -> Option<Value> {
352 match self.split(node) {
353 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().default_value(inner),
354 None => self.outer.default_value(node),
355 }
356 }
357
358 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
359 match self.split(node) {
360 Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().metadata(inner, key),
361 None => self.outer.metadata(node, key),
362 }
363 }
364
365 fn provenance(&self, node: NodeId) -> quarb::Provenance {
372 match self.split(node) {
373 Some((g, inner)) => {
374 let (inner_prov, outer_leaf) = {
378 let grafts = self.grafts.borrow();
379 (
380 grafts[g].inner.adapter().provenance(inner),
381 grafts[g].outer,
382 )
383 };
384 inner_prov.or(self.outer.provenance(outer_leaf))
385 }
386 None => self.outer.provenance(node),
387 }
388 }
389
390 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
391 match self.split(node) {
392 Some((g, inner)) => {
393 let t = self.grafts.borrow()[g]
394 .inner
395 .adapter()
396 .resolve(inner, property, hint)?;
397 Some(self.wrap(g, t))
398 }
399 None => self.outer.resolve(node, property, hint),
400 }
401 }
402
403 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
404 match self.split(node) {
405 Some((g, inner)) => {
406 let ls: Vec<(String, NodeId)> = {
407 let grafts = self.grafts.borrow();
408 grafts[g].inner.adapter().links(inner)
409 };
410 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
411 }
412 None => self.outer.links(node),
413 }
414 }
415
416 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
417 match self.split(node) {
418 Some((g, inner)) => {
419 let ls: Vec<(String, NodeId)> = {
420 let grafts = self.grafts.borrow();
421 grafts[g].inner.adapter().backlinks(inner)
422 };
423 ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
424 }
425 None => self.outer.backlinks(node),
426 }
427 }
428
429 fn link_property(
430 &self,
431 source: NodeId,
432 label: &str,
433 target: NodeId,
434 name: &str,
435 ) -> Option<Value> {
436 match (self.split(source), self.split(target)) {
439 (Some((g, src)), Some((gt, tgt))) if g == gt => self.grafts.borrow()[g]
440 .inner
441 .adapter()
442 .link_property(src, label, tgt, name),
443 (None, None) => self.outer.link_property(source, label, target, name),
444 _ => None,
445 }
446 }
447}