use quarb::source::SourceDecoding;
use quarb::{AstAdapter, NodeId, Value};
use std::cell::RefCell;
use std::collections::HashMap;
use std::path::PathBuf;
enum Inner {
Json(quarb_json::JsonAdapter),
Xml(quarb_xml::XmlAdapter),
Html(quarb_html::HtmlAdapter),
Text(quarb_text::TextModel),
Csv(quarb_csv::CsvAdapter),
#[cfg(feature = "source")]
Syntax(quarb_tree_sitter::TreeSitterAdapter),
#[cfg(feature = "source")]
Code(quarb_code::CodeModel),
Archive(Box<ComposeAdapter<quarb_archive::ArchiveAdapter>>),
}
impl Inner {
fn adapter(&self) -> &dyn AstAdapter {
match self {
Inner::Json(a) => a,
Inner::Xml(a) => a,
Inner::Html(a) => a,
Inner::Text(a) => a,
Inner::Csv(a) => a,
#[cfg(feature = "source")]
Inner::Syntax(a) => a,
#[cfg(feature = "source")]
Inner::Code(a) => a,
Inner::Archive(a) => &**a,
}
}
fn locator(&self, node: NodeId) -> String {
match self {
Inner::Json(a) => a.pointer(node),
Inner::Xml(a) => a.locator(node),
Inner::Html(a) => a.locator(node),
Inner::Text(a) => a.locator(node),
Inner::Csv(a) => a.locator(node),
#[cfg(feature = "source")]
Inner::Syntax(a) => a.locator(node),
#[cfg(feature = "source")]
Inner::Code(a) => a.locator(node),
Inner::Archive(a) => a.locator(node, |o| a.outer().locator(o)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SourceGraft {
#[default]
Syntax,
Code,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DocumentGraft {
#[default]
Dom,
Text,
}
pub fn declared_encoding(name: &str, bytes: &[u8]) -> Option<String> {
let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
match ext.as_str() {
"xml" | "svg" | "xhtml" | "fb2" | "tei" | "rss" | "atom" | "opml" | "xsd" | "xsl" => {
quarb_xml::declared_encoding(bytes)
}
"html" | "htm" => quarb_html::declared_encoding(bytes),
"rtf" => quarb_text_rtf::declared_encoding(bytes),
_ => quarb_xml::declared_encoding(bytes).or_else(|| quarb_html::declared_encoding(bytes)),
}
}
pub fn source_decoding(
explicit: Option<&str>,
) -> Result<SourceDecoding, quarb::source::SourceError> {
SourceDecoding::default()
.with_declared(declared_encoding)
.with_option(explicit)
}
pub fn read_source(
path: &std::path::Path,
explicit: Option<&str>,
) -> std::io::Result<Result<quarb::source::Decoded, quarb::source::SourceError>> {
let bytes = std::fs::read(path)?;
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
Ok(source_decoding(explicit).and_then(|d| d.decode(name, &bytes)))
}
fn archive_name(name: &str) -> bool {
let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
matches!(
ext.as_str(),
"zip" | "tar" | "tgz" | "gz" | "7z" | "xz" | "txz" | "bz2" | "tbz2" | "zst" | "tzst"
)
}
fn parse_inner(
name: &str,
content: &str,
graft: SourceGraft,
doc: DocumentGraft,
csv: quarb_csv::CsvOptions,
) -> Option<Inner> {
#[cfg(not(feature = "source"))]
let _ = graft;
let ext = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
match ext.as_str() {
"json" => {
return quarb_json::JsonAdapter::parse(content)
.ok()
.map(Inner::Json);
}
"jsonl" | "ndjson" => {
return quarb_json::JsonAdapter::parse_lines(content)
.ok()
.map(Inner::Json);
}
"xml" | "svg" | "xhtml" => {
#[cfg(feature = "koine")]
if doc == DocumentGraft::Text
&& quarb_text_koine::detect_xml_kind(content) == Some("fb2")
&& let Ok(m) = quarb_text_koine::parse_xml_as(content, "fb2")
{
return Some(Inner::Text(m));
}
return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
}
"fb2" => {
#[cfg(feature = "koine")]
if doc == DocumentGraft::Text
&& let Ok(m) = quarb_text_koine::parse_xml_as(content, "fb2")
{
return Some(Inner::Text(m));
}
return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
}
"html" | "htm" => {
return Some(match doc {
DocumentGraft::Dom => Inner::Html(quarb_html::HtmlAdapter::parse(content)),
DocumentGraft::Text => Inner::Text(quarb_text_html::parse(content)),
});
}
"yaml" | "yml" => return quarb_yaml::parse(content).ok().map(Inner::Json),
"toml" => return quarb_toml::parse(content).ok().map(Inner::Json),
"md" | "markdown" => {
return Some(match doc {
DocumentGraft::Dom => Inner::Html(quarb_markdown::parse(content)),
DocumentGraft::Text => Inner::Text(quarb_text_markdown::parse(content)),
});
}
"txt" => return Some(Inner::Text(quarb_text::TextModel::parse_plain(content))),
"csv" => {
return quarb_csv::CsvAdapter::parse_declared(content, b',', csv)
.ok()
.map(Inner::Csv);
}
"tsv" => {
return quarb_csv::CsvAdapter::parse_declared(content, b'\t', csv)
.ok()
.map(Inner::Csv);
}
#[cfg(feature = "source")]
ext if quarb_tree_sitter::supported(ext) => {
return match graft {
SourceGraft::Syntax => quarb_tree_sitter::TreeSitterAdapter::parse(content, ext)
.ok()
.map(Inner::Syntax),
SourceGraft::Code => quarb_code::CodeModel::parse(content, ext)
.ok()
.map(Inner::Code),
};
}
_ => {}
}
let t = content.trim_start();
if t.starts_with('{') || t.starts_with('[') {
return quarb_json::JsonAdapter::parse(content)
.ok()
.map(Inner::Json);
}
if t.starts_with("<?xml") {
return quarb_xml::XmlAdapter::parse(content).ok().map(Inner::Xml);
}
None
}
struct Graft {
outer: NodeId,
inner: Inner,
}
const GRAFT_BIT: u64 = 1 << 55;
pub type DocumentReader = std::rc::Rc<dyn Fn(&std::path::Path) -> Option<quarb_text::TextModel>>;
pub struct ComposeAdapter<A: AstAdapter> {
outer: A,
grafts: RefCell<Vec<Graft>>,
probed: RefCell<HashMap<NodeId, Option<usize>>>,
interned: RefCell<HashMap<(usize, NodeId), NodeId>>,
reverse: RefCell<Vec<(usize, NodeId)>>,
source_path: Option<fn(&A, NodeId) -> Option<PathBuf>>,
source_graft: SourceGraft,
document_graft: DocumentGraft,
document_url: Option<fn(&A, NodeId) -> Option<String>>,
urls: RefCell<Option<HashMap<String, NodeId>>>,
document_reader: Option<DocumentReader>,
decoding: SourceDecoding,
decoded: RefCell<HashMap<usize, (String, usize)>>,
csv: quarb_csv::CsvOptions,
}
impl<A: AstAdapter> ComposeAdapter<A> {
pub fn new(outer: A) -> Self {
ComposeAdapter {
outer,
grafts: RefCell::new(Vec::new()),
probed: RefCell::new(HashMap::new()),
interned: RefCell::new(HashMap::new()),
reverse: RefCell::new(Vec::new()),
source_path: None,
source_graft: SourceGraft::default(),
document_graft: DocumentGraft::default(),
document_url: None,
urls: RefCell::new(None),
document_reader: None,
decoding: SourceDecoding::default(),
decoded: RefCell::new(HashMap::new()),
csv: quarb_csv::CsvOptions::default(),
}
}
pub fn with_csv_options(mut self, csv: quarb_csv::CsvOptions) -> Self {
self.csv = csv;
self
}
pub fn with_decoding(mut self, decoding: SourceDecoding) -> Self {
self.decoding = decoding;
self
}
pub fn with_document_reader(mut self, reader: DocumentReader) -> Self {
self.document_reader = Some(reader);
self
}
pub fn with_document_urls(mut self, document_url: fn(&A, NodeId) -> Option<String>) -> Self {
self.document_url = Some(document_url);
self
}
fn leaf_by_url(&self, url: &str) -> Option<NodeId> {
let f = self.document_url?;
if self.urls.borrow().is_none() {
let mut map = HashMap::new();
let mut stack = vec![self.outer.root()];
while let Some(n) = stack.pop() {
let kids = self.outer.children(n);
if kids.is_empty() {
if let Some(u) = f(&self.outer, n) {
map.entry(u.split('#').next().unwrap_or(&u).to_string())
.or_insert(n);
}
} else {
stack.extend(kids);
}
}
*self.urls.borrow_mut() = Some(map);
}
self.urls.borrow().as_ref()?.get(url).copied()
}
pub fn with_source_graft(mut self, graft: SourceGraft) -> Self {
self.source_graft = graft;
self
}
pub fn with_document_graft(mut self, graft: DocumentGraft) -> Self {
self.document_graft = graft;
self
}
pub fn with_source_paths(outer: A, source_path: fn(&A, NodeId) -> Option<PathBuf>) -> Self {
ComposeAdapter {
source_path: Some(source_path),
..Self::new(outer)
}
}
pub fn outer(&self) -> &A {
&self.outer
}
pub fn locator(&self, node: NodeId, outer_locator: impl Fn(NodeId) -> String) -> String {
match self.split(node) {
None => outer_locator(node),
Some((g, inner)) => {
let grafts = self.grafts.borrow();
let graft = &grafts[g];
format!(
"{}!{}",
outer_locator(graft.outer),
graft.inner.locator(inner)
)
}
}
}
fn graft_fill(&self, outer_leaf: NodeId, inner_path: String) -> quarb::Provenance {
let mut fill = self.outer.provenance(outer_leaf).as_source_rung();
fill.path = Some(inner_path);
fill
}
fn split(&self, node: NodeId) -> Option<(usize, NodeId)> {
if node.0 & GRAFT_BIT == 0 {
return None;
}
self.reverse
.borrow()
.get((node.0 & !GRAFT_BIT) as usize)
.copied()
}
fn intern(&self, graft: usize, inner: NodeId) -> NodeId {
if let Some(&id) = self.interned.borrow().get(&(graft, inner)) {
return id;
}
let mut rev = self.reverse.borrow_mut();
let id = NodeId(GRAFT_BIT | rev.len() as u64);
rev.push((graft, inner));
self.interned.borrow_mut().insert((graft, inner), id);
id
}
fn graft_at(&self, node: NodeId) -> Option<usize> {
if let Some(&g) = self.probed.borrow().get(&node) {
return g;
}
let g = (|| {
if !self.outer.children(node).is_empty() {
return None;
}
let name = self.outer.name(node)?;
if archive_name(&name)
&& let Some(path_of) = self.source_path
&& let Some(path) = path_of(&self.outer, node)
&& let Ok(a) =
quarb_archive::ArchiveAdapter::open_with_decoding(&path, self.decoding.clone())
{
let inner = Inner::Archive(Box::new(
ComposeAdapter::new(a)
.with_source_graft(self.source_graft)
.with_document_graft(self.document_graft)
.with_decoding(self.decoding.clone())
.with_csv_options(self.csv),
));
let mut grafts = self.grafts.borrow_mut();
grafts.push(Graft { outer: node, inner });
return Some(grafts.len() - 1);
}
if let Some(read) = &self.document_reader
&& let Some(path_of) = self.source_path
&& let Some(path) = path_of(&self.outer, node)
&& let Some(model) = read(&path)
{
let mut grafts = self.grafts.borrow_mut();
grafts.push(Graft {
outer: node,
inner: Inner::Text(model),
});
return Some(grafts.len() - 1);
}
let mut record = None;
let content = match self
.source_path
.and_then(|path_of| path_of(&self.outer, node))
.and_then(|p| std::fs::read(p).ok())
{
Some(bytes) => {
let d = self.decoding.decode(&name, &bytes).ok()?;
record = Some((d.encoding, d.replaced));
d.text
}
None => match self.outer.default_value(node)? {
Value::Str(s) => s,
_ => return None,
},
};
let mut inner = parse_inner(
&name,
&content,
self.source_graft,
self.document_graft,
self.csv,
)?;
if let (Inner::Text(model), Some(f)) = (&mut inner, self.document_url)
&& let Some(u) = f(&self.outer, node)
{
model.set_document_url(&u);
}
let mut grafts = self.grafts.borrow_mut();
grafts.push(Graft { outer: node, inner });
if let Some(r) = record {
self.decoded.borrow_mut().insert(grafts.len() - 1, r);
}
Some(grafts.len() - 1)
})();
self.probed.borrow_mut().insert(node, g);
g
}
fn wrap(&self, graft: usize, inner: NodeId) -> NodeId {
let grafts = self.grafts.borrow();
if inner == grafts[graft].inner.adapter().root() {
grafts[graft].outer
} else {
drop(grafts);
self.intern(graft, inner)
}
}
}
impl<A: AstAdapter> AstAdapter for ComposeAdapter<A> {
fn root(&self) -> NodeId {
self.outer.root()
}
fn children(&self, node: NodeId) -> Vec<NodeId> {
match self.split(node) {
Some((g, inner)) => {
let ids: Vec<NodeId> = {
let grafts = self.grafts.borrow();
grafts[g].inner.adapter().children(inner)
};
ids.into_iter().map(|c| self.wrap(g, c)).collect()
}
None => {
let outer = self.outer.children(node);
if !outer.is_empty() {
return outer;
}
match self.graft_at(node) {
Some(g) => {
let ids: Vec<NodeId> = {
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
a.children(a.root())
};
ids.into_iter().map(|c| self.wrap(g, c)).collect()
}
None => Vec::new(),
}
}
}
}
fn children_named(&self, node: NodeId, name: &str) -> Vec<NodeId> {
match self.split(node) {
Some((g, inner)) => {
let ids: Vec<NodeId> = {
let grafts = self.grafts.borrow();
grafts[g].inner.adapter().children_named(inner, name)
};
ids.into_iter().map(|c| self.wrap(g, c)).collect()
}
None => {
let named = self.outer.children_named(node, name);
if !named.is_empty() {
return named;
}
if self.source_path.is_none()
&& !matches!(self.outer.default_value(node), Some(Value::Str(_)))
{
return Vec::new();
}
self.children(node)
.into_iter()
.filter(|&c| self.answers_to(c, name))
.collect()
}
}
}
fn name(&self, node: NodeId) -> Option<String> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().name(inner),
None => self.outer.name(node),
}
}
fn parent(&self, node: NodeId) -> Option<NodeId> {
match self.split(node) {
Some((g, inner)) => {
let p = self.grafts.borrow()[g].inner.adapter().parent(inner)?;
Some(self.wrap(g, p))
}
None => self.outer.parent(node),
}
}
fn child_host(&self, node: NodeId) -> NodeId {
match self.split(node) {
Some((g, inner)) => {
let h = self.grafts.borrow()[g].inner.adapter().child_host(inner);
self.wrap(g, h)
}
None => self.outer.child_host(node),
}
}
fn properties(&self, node: NodeId) -> Vec<(String, Value)> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().properties(inner),
None => self.outer.properties(node),
}
}
fn walks_for_references(&self) -> bool {
self.outer.walks_for_references()
}
fn can_refer_to(&self, source: NodeId, target: NodeId) -> Option<bool> {
match (self.split(source), self.split(target)) {
(None, None) => self.outer.can_refer_to(source, target),
(Some((g, s)), Some((h, t))) if g == h => {
self.grafts.borrow()[g].inner.adapter().can_refer_to(s, t)
}
_ => self.ref_property(source).map(|_| true),
}
}
fn reference_properties(&self, source: NodeId, target: NodeId) -> Vec<String> {
match (self.split(source), self.split(target)) {
(None, None) => self.outer.reference_properties(source, target),
(Some((g, s)), Some((h, t))) if g == h => self.grafts.borrow()[g]
.inner
.adapter()
.reference_properties(s, t),
_ => self.ref_property(source).into_iter().collect(),
}
}
fn index_referrers(
&self,
scope: &str,
property: &str,
label: Option<&str>,
target: NodeId,
) -> Option<Vec<NodeId>> {
match self.split(target) {
None => self.outer.index_referrers(scope, property, label, target),
Some(_) => None,
}
}
fn tree_identities(&self) -> Vec<(String, NodeId)> {
self.outer.tree_identities()
}
fn reverse_label(&self, target: NodeId) -> Option<String> {
match self.split(target) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().reverse_label(inner),
None => self.outer.reverse_label(target),
}
}
fn resolve_needs_label(&self, node: NodeId, property: &str) -> bool {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.resolve_needs_label(inner, property),
None => self.outer.resolve_needs_label(node, property),
}
}
fn property_names(&self, node: NodeId) -> Vec<String> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.property_names(inner),
None => self.outer.property_names(node),
}
}
fn resolves_references(&self) -> bool {
self.outer.resolves_references()
|| self
.grafts
.borrow()
.iter()
.any(|g| g.inner.adapter().resolves_references())
}
fn non_tree_nodes(&self) -> Vec<NodeId> {
let mut out = self.outer.non_tree_nodes();
let grafts = self.grafts.borrow();
for (g, graft) in grafts.iter().enumerate() {
out.extend(
graft
.inner
.adapter()
.non_tree_nodes()
.into_iter()
.map(|n| self.wrap(g, n)),
);
}
out
}
fn answers_to(&self, node: NodeId, name: &str) -> bool {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.answers_to(inner, name),
None => self.outer.answers_to(node, name),
}
}
fn traits(&self, node: NodeId) -> Vec<String> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().traits(inner),
None => {
let mut out = self.outer.traits(node);
if let Some(g) = self.graft_at(node) {
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
for t in a.traits(a.root()) {
if !out.contains(&t) {
out.push(t);
}
}
}
out
}
}
}
fn property(&self, node: NodeId, name: &str) -> Option<Value> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.property(inner, name),
None => self.outer.property(node, name).or_else(|| {
let g = self.graft_at(node)?;
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
a.property(a.root(), name)
}),
}
}
fn default_value(&self, node: NodeId) -> Option<Value> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().default_value(inner),
None => {
if self.decoding.explicit.is_some()
&& self.outer.children(node).is_empty()
&& let Some(path_of) = self.source_path
&& let Some(p) = path_of(&self.outer, node)
&& p.is_file()
&& let Ok(bytes) = std::fs::read(&p)
{
let name = self.outer.name(node).unwrap_or_default();
return self
.decoding
.decode(&name, &bytes)
.ok()
.map(|d| Value::Str(d.text));
}
self.outer.default_value(node)
}
}
}
fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().metadata(inner, key),
None => self.outer.metadata(node, key).or_else(|| {
let g = self.graft_at(node)?;
if matches!(key, "encoding" | "replaced") {
if !self.decoded.borrow().contains_key(&g)
&& let Some(path_of) = self.source_path
&& let Some(p) = path_of(&self.outer, node)
&& let Ok(bytes) = std::fs::read(&p)
{
let name = self.outer.name(node).unwrap_or_default();
if let Ok(d) = self.decoding.decode(&name, &bytes) {
self.decoded
.borrow_mut()
.insert(g, (d.encoding, d.replaced));
}
}
if let Some((enc, replaced)) = self.decoded.borrow().get(&g) {
return Some(match key {
"encoding" => Value::Str(enc.clone()),
_ => Value::Int(*replaced as i64),
});
}
}
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
a.metadata(a.root(), key)
}),
}
}
fn aliased_metadata(&self, node: NodeId) -> &'static [&'static str] {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.aliased_metadata(inner),
None => self.outer.aliased_metadata(node),
}
}
fn provenance(&self, node: NodeId) -> quarb::Provenance {
match self.split(node) {
Some((g, inner)) => {
let (inner_prov, outer_leaf, inner_path) = {
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
(
a.provenance(inner),
grafts[g].outer,
quarb::name_path(a, inner),
)
};
inner_prov.or(self.graft_fill(outer_leaf, inner_path))
}
None => self.outer.provenance(node),
}
}
fn provenance_list(&self, node: NodeId) -> quarb::ProvenanceList {
match self.split(node) {
Some((g, inner)) => {
let (inner_list, outer_leaf, inner_path) = {
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
(
a.provenance_list(inner),
grafts[g].outer,
quarb::name_path(a, inner),
)
};
inner_list.or(self.graft_fill(outer_leaf, inner_path))
}
None => self.outer.provenance_list(node),
}
}
fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
match self.split(node) {
Some((g, inner)) => {
let t = self.grafts.borrow()[g]
.inner
.adapter()
.resolve(inner, property, hint)?;
Some(self.wrap(g, t))
}
None => self.outer.resolve(node, property, hint),
}
}
fn has_trait(&self, node: NodeId, name: &str) -> bool {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.has_trait(inner, name),
None => {
self.outer.has_trait(node, name)
|| self.graft_at(node).is_some_and(|g| {
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
a.has_trait(a.root(), name)
})
}
}
}
fn ref_property(&self, node: NodeId) -> Option<String> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().ref_property(inner),
None => self.outer.ref_property(node),
}
}
fn external_ref(
&self,
node: NodeId,
property: &str,
hint: Option<&str>,
) -> Option<quarb::ExternalRef> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.external_ref(inner, property, hint),
None => self.outer.external_ref(node, property, hint),
}
}
fn ref_label(&self, node: NodeId, property: &str) -> Option<String> {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g]
.inner
.adapter()
.ref_label(inner, property),
None => self.outer.ref_label(node, property),
}
}
fn resolve_fragment(&self, node: NodeId, fragment: &str) -> Option<NodeId> {
match self.split(node) {
Some((g, inner)) => {
let t = self.grafts.borrow()[g]
.inner
.adapter()
.resolve_fragment(inner, fragment)?;
Some(self.wrap(g, t))
}
None => match self.graft_at(node) {
Some(g) => {
let t = {
let grafts = self.grafts.borrow();
let a = grafts[g].inner.adapter();
a.resolve_fragment(a.root(), fragment)?
};
Some(self.wrap(g, t))
}
None => self.outer.resolve_fragment(node, fragment),
},
}
}
fn document_by_ref(&self, id: &str) -> Option<NodeId> {
if let Some(leaf) = self.leaf_by_url(id) {
let _ = self.graft_at(leaf);
return Some(leaf);
}
self.outer.document_by_ref(id)
}
fn reverse_resolve(
&self,
node: NodeId,
property: Option<&str>,
hint: Option<&str>,
) -> Option<Vec<NodeId>> {
match self.split(node) {
Some((g, inner)) => {
let v = self.grafts.borrow()[g]
.inner
.adapter()
.reverse_resolve(inner, property, hint)?;
Some(v.into_iter().map(|t| self.wrap(g, t)).collect())
}
None => self.outer.reverse_resolve(node, property, hint),
}
}
fn descendants_named(&self, node: NodeId, name: &str) -> Option<Vec<(NodeId, usize)>> {
let (g, inner) = self.split(node)?;
let v = self.grafts.borrow()[g]
.inner
.adapter()
.descendants_named(inner, name)?;
Some(v.into_iter().map(|(t, d)| (self.wrap(g, t), d)).collect())
}
fn descendants_where(
&self,
node: NodeId,
name: &str,
property: &str,
value: &Value,
) -> Option<Vec<(NodeId, usize)>> {
let (g, inner) = self.split(node)?;
let v = self.grafts.borrow()[g]
.inner
.adapter()
.descendants_where(inner, name, property, value)?;
Some(v.into_iter().map(|(t, d)| (self.wrap(g, t), d)).collect())
}
fn has_node(&self, node: NodeId) -> bool {
match self.split(node) {
Some((g, inner)) => self.grafts.borrow()[g].inner.adapter().has_node(inner),
None => self.outer.has_node(node),
}
}
fn unit_scale(&self, expr: &str) -> Option<(f64, String)> {
self.outer.unit_scale(expr)
}
fn allow_shell(&self) -> bool {
self.outer.allow_shell()
}
fn quantifier_bound(&self) -> usize {
self.outer.quantifier_bound()
}
fn invocation_instant(&self) -> Option<(i64, u32)> {
self.outer.invocation_instant()
}
fn prefetch_links(&self, nodes: &[NodeId], dir: quarb::LinkDir) {
let outer: Vec<NodeId> = nodes
.iter()
.copied()
.filter(|n| self.split(*n).is_none())
.collect();
if !outer.is_empty() {
self.outer.prefetch_links(&outer, dir);
}
}
fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
match self.split(node) {
Some((g, inner)) => {
let ls: Vec<(String, NodeId)> = {
let grafts = self.grafts.borrow();
grafts[g].inner.adapter().links(inner)
};
ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
}
None => self.outer.links(node),
}
}
fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
match self.split(node) {
Some((g, inner)) => {
let ls: Vec<(String, NodeId)> = {
let grafts = self.grafts.borrow();
grafts[g].inner.adapter().backlinks(inner)
};
ls.into_iter().map(|(l, n)| (l, self.wrap(g, n))).collect()
}
None => self.outer.backlinks(node),
}
}
fn link_property(
&self,
source: NodeId,
label: &str,
target: NodeId,
name: &str,
) -> Option<Value> {
match (self.split(source), self.split(target)) {
(Some((g, src)), Some((gt, tgt))) if g == gt => self.grafts.borrow()[g]
.inner
.adapter()
.link_property(src, label, tgt, name),
(None, None) => self.outer.link_property(source, label, target, name),
_ => None,
}
}
}