Skip to main content

rdfx/interpretation/
mod.rs

1//! Resource interpretations.
2use std::borrow::Cow;
3
4use crate::{BlankId, CowId, CowLiteral, CowLocalTerm, CowTerm, IdRef, LiteralRef, LocalTermRef, TermRef, TripleTerm};
5
6/// Borrow-or-own view of a triple term's three components, used by
7/// [`ReverseInterpretation::triple_term_components_view`].
8pub type TripleTermView<'a, R> = (Cow<'a, R>, Cow<'a, R>, Cow<'a, R>);
9
10mod r#impl;
11pub use r#impl::*;
12
13pub mod fallible;
14pub use fallible::FallibleInterpretation;
15
16mod iri;
17pub use iri::*;
18
19mod blank_id;
20pub use blank_id::*;
21
22mod literal;
23pub use literal::*;
24
25mod id;
26pub use id::*;
27
28mod term;
29pub use term::*;
30
31use iri_rs::{Iri, IriBuf};
32
33/// RDF resource interpretation.
34pub trait Interpretation {
35    type Resource;
36
37    fn iri(&self, iri: Iri<&str>) -> Option<Self::Resource>;
38
39    fn literal<'a>(&self, literal: impl Into<LiteralRef<'a>>) -> Option<Self::Resource>;
40
41    fn term<'a>(&self, term: impl Into<TermRef<'a>>) -> Option<Self::Resource> {
42        match term.into() {
43            TermRef::Iri(iri) => self.iri(iri),
44            TermRef::Literal(l) => self.literal(l),
45        }
46    }
47}
48
49impl<I: Interpretation> Interpretation for &I {
50    type Resource = I::Resource;
51
52    fn iri(&self, iri: Iri<&str>) -> Option<Self::Resource> {
53        I::iri(*self, iri)
54    }
55
56    fn literal<'a>(&self, literal: impl Into<LiteralRef<'a>>) -> Option<Self::Resource> {
57        I::literal(*self, literal)
58    }
59}
60
61impl<I: Interpretation> Interpretation for &mut I {
62    type Resource = I::Resource;
63
64    fn iri(&self, iri: Iri<&str>) -> Option<Self::Resource> {
65        I::iri(*self, iri)
66    }
67
68    fn literal<'a>(&self, literal: impl Into<LiteralRef<'a>>) -> Option<Self::Resource> {
69        I::literal(*self, literal)
70    }
71}
72
73/// Interpretation that can return an iterator over the known RDF resources.
74pub trait TraversableInterpretation: Interpretation {
75    type Resources<'a>: Iterator<Item = &'a Self::Resource>
76    where
77        Self: 'a;
78
79    fn resources(&self) -> Self::Resources<'_>;
80}
81
82/// Interpretation that can spawn fresh new resources.
83pub trait GenerativeInterpretation: Interpretation {
84    /// Create a new resource.
85    fn new_resource(&mut self) -> Self::Resource;
86}
87
88/// Interpretation that can spawn fresh new resources.
89pub trait ConstGenerativeInterpretation: Interpretation {
90    /// Create a new resource.
91    fn new_resource(&self) -> Self::Resource;
92}
93
94impl<I: ConstGenerativeInterpretation> GenerativeInterpretation for I {
95    fn new_resource(&mut self) -> Self::Resource {
96        ConstGenerativeInterpretation::new_resource(self)
97    }
98}
99
100/// Mutable interpretation.
101pub trait InterpretationMut: Interpretation {
102    fn insert_iri<'a>(&mut self, iri: impl Into<Cow<'a, IriBuf>>) -> Self::Resource;
103
104    fn insert_literal<'a>(&mut self, literal: impl Into<CowLiteral<'a>>) -> Self::Resource;
105
106    fn insert_term<'a>(&mut self, term: impl Into<CowTerm<'a>>) -> Self::Resource {
107        match term.into() {
108            CowTerm::Iri(iri) => self.insert_iri(iri),
109            CowTerm::Literal(literal) => self.insert_literal(literal),
110        }
111    }
112}
113
114/// Reverse interpretation.
115pub trait ReverseInterpretation: Interpretation {
116    type Iris<'a>: Iterator<Item = Cow<'a, IriBuf>>
117    where
118        Self: 'a;
119    type Literals<'a>: Iterator<Item = CowLiteral<'a>>
120    where
121        Self: 'a;
122
123    fn iris_of<'a>(&'a self, resource: &'a Self::Resource) -> Self::Iris<'a>;
124
125    fn literals_of<'a>(&'a self, resource: &'a Self::Resource) -> Self::Literals<'a>;
126
127    fn terms_of<'a>(&'a self, resource: &'a Self::Resource) -> TermsOf<'a, Self> {
128        TermsOf {
129            iris: self.iris_of(resource),
130            literals: self.literals_of(resource),
131        }
132    }
133
134    /// RDF 1.2: returns the components of a triple-term resource as
135    /// `(subject, predicate, object)` if `resource` denotes a triple term.
136    /// Default returns `None` (no triple-term recognition); interpretations
137    /// that store triple terms should override.
138    ///
139    /// Components are returned by value because the underlying triple-term
140    /// body may use a different concrete type from `Self::Resource`
141    /// (e.g. lexical bodies use `Id` for the subject, `IriBuf` for the
142    /// predicate, both wrapped into `Self::Resource` for uniformity).
143    ///
144    /// For zero-clone descent during isomorphism — the dominant hot path —
145    /// override [`Self::triple_term_components_view`] instead (or in addition).
146    fn triple_term_components(&self, _resource: &Self::Resource) -> Option<(Self::Resource, Self::Resource, Self::Resource)> {
147        None
148    }
149
150    /// Borrow-or-own view of triple-term components — the zero-clone
151    /// counterpart of [`Self::triple_term_components`].
152    ///
153    /// Implementations whose component types are storage-natural to
154    /// `Self::Resource` (e.g. an interpretation that stores triple terms
155    /// using `LocalTerm`-typed components) should override and return
156    /// [`Cow::Borrowed`] for those components. The default delegates to
157    /// [`Self::triple_term_components`] and wraps each component in [`Cow::Owned`].
158    ///
159    /// Used internally by [`graph_equivalent_with`](crate::dataset::isomorphism::graph_equivalent_with)
160    /// / [`dataset_equivalent_with`](crate::dataset::isomorphism::dataset_equivalent_with)
161    /// during triple-term descent — overriding here removes per-descent
162    /// clones for triple-term-heavy graphs.
163    fn triple_term_components_view<'a>(&'a self, resource: &'a Self::Resource) -> Option<TripleTermView<'a, Self::Resource>>
164    where
165        Self::Resource: Clone,
166    {
167        self.triple_term_components(resource).map(|(s, p, o)| (Cow::Owned(s), Cow::Owned(p), Cow::Owned(o)))
168    }
169
170    fn is_blank_id(&self, resource: &Self::Resource) -> bool {
171        self.terms_of(resource).next().is_none() && self.triple_term_components(resource).is_none()
172    }
173}
174
175pub struct TermsOf<'a, I: 'a + ?Sized + ReverseInterpretation> {
176    iris: I::Iris<'a>,
177    literals: I::Literals<'a>,
178}
179
180impl<'a, I: 'a + ?Sized + ReverseInterpretation> Iterator for TermsOf<'a, I> {
181    type Item = CowTerm<'a>;
182
183    fn next(&mut self) -> Option<Self::Item> {
184        self.iris.next().map(CowTerm::Iri).or_else(|| self.literals.next().map(CowTerm::Literal))
185    }
186}
187
188pub trait LocalInterpretation: Interpretation {
189    fn blank_id(&self, blank_id: &BlankId) -> Option<Self::Resource>;
190
191    fn local_term<'a>(&self, term: impl Into<LocalTermRef<'a>>) -> Option<Self::Resource> {
192        match term.into() {
193            LocalTermRef::BlankId(blank_id) => self.blank_id(blank_id),
194            LocalTermRef::Named(term) => self.term(term),
195            LocalTermRef::Triple(t) => self.triple_term(t.clone()),
196        }
197    }
198
199    /// Looks up a triple-term resource (RDF 1.2). Default implementation
200    /// returns `None` — interpretations that store triple terms should
201    /// override.
202    fn triple_term(&self, _t: TripleTerm) -> Option<Self::Resource> {
203        None
204    }
205
206    fn id<'a>(&self, id: impl Into<IdRef<'a>>) -> Option<Self::Resource> {
207        match id.into() {
208            IdRef::BlankId(blank_id) => self.blank_id(blank_id),
209            IdRef::Iri(iri) => self.iri(iri),
210        }
211    }
212}
213
214pub trait LocalInterpretationMut: InterpretationMut {
215    fn insert_blank_id<'a>(&mut self, blank_id: impl Into<Cow<'a, BlankId>>) -> Self::Resource;
216
217    fn insert_local_term<'a>(&mut self, term: impl Into<CowLocalTerm<'a>>) -> Self::Resource {
218        match term.into() {
219            CowLocalTerm::BlankId(blank_id) => self.insert_blank_id(blank_id),
220            CowLocalTerm::Named(term) => self.insert_term(term),
221            CowLocalTerm::Triple(t) => self.insert_triple_term(t.into_owned()),
222        }
223    }
224
225    /// Inserts a triple-term resource (RDF 1.2). Default implementation
226    /// panics — interpretations that need to ingest triple-term-bearing
227    /// graphs must override this method.
228    fn insert_triple_term(&mut self, _t: TripleTerm) -> Self::Resource {
229        unimplemented!("triple-term insertion not implemented by this LocalInterpretationMut")
230    }
231
232    fn insert_id<'a>(&mut self, term: impl Into<CowId<'a>>) -> Self::Resource {
233        match term.into() {
234            CowId::BlankId(blank_id) => self.insert_blank_id(blank_id),
235            CowId::Iri(iri) => self.insert_iri(iri),
236        }
237    }
238}
239
240pub trait ReverseLocalInterpretation: ReverseInterpretation {
241    type BlankIds<'a>: Iterator<Item = Cow<'a, BlankId>>
242    where
243        Self: 'a;
244
245    fn blank_ids_of<'a>(&'a self, resource: &'a Self::Resource) -> Self::BlankIds<'a>;
246
247    fn local_terms_of<'a>(&'a self, resource: &'a Self::Resource) -> LocalTermsOf<'a, Self> {
248        LocalTermsOf {
249            terms: self.terms_of(resource),
250            blank_ids: self.blank_ids_of(resource),
251        }
252    }
253}
254
255pub struct LocalTermsOf<'a, I: 'a + ?Sized + ReverseLocalInterpretation> {
256    terms: TermsOf<'a, I>,
257    blank_ids: I::BlankIds<'a>,
258}
259
260impl<'a, I: 'a + ?Sized + ReverseLocalInterpretation> Iterator for LocalTermsOf<'a, I> {
261    type Item = CowLocalTerm<'a>;
262
263    fn next(&mut self) -> Option<Self::Item> {
264        self.terms
265            .next()
266            .map(CowLocalTerm::Named)
267            .or_else(|| self.blank_ids.next().map(CowLocalTerm::BlankId))
268    }
269}