sophia_api/
dataset.rs

1//! An RDF dataset is composed of a default [dataset](crate::dataset),
2//! and zero or more named graphs, each associated with a dataset name.
3//!
4//! Another way to look at it is as a collection of [quad](crate::quad)s.
5//!
6//! This module provides [reusable abstractions](#traits)
7//! for different kinds of datasets,
8//! as well as a few implementations for them.
9
10use crate::graph::adapter::{DatasetGraph, PartialUnionGraph, UnionGraph};
11use crate::quad::{iter_spog, Quad};
12use crate::source::{IntoQuadSource, QuadSource, StreamResult};
13use crate::term::matcher::{GraphNameMatcher, TermMatcher};
14use crate::term::{GraphName, SimpleTerm, Term};
15use resiter::{filter::*, filter_map::*, flat_map::*, map::*};
16use std::error::Error;
17
18mod _foreign_impl;
19pub mod adapter;
20#[cfg(any(test, feature = "test_macro"))]
21#[macro_use]
22pub mod test;
23
24/// Type alias for results produced by a dataset.
25pub type DResult<D, T> = Result<T, <D as Dataset>::Error>;
26/// Type alias for fallible quad iterators produced by a dataset.
27///
28/// See [`Dataset::quads`] for more information about how to use it.
29pub type DQuadSource<'a, D> = Box<dyn Iterator<Item = DResult<D, <D as Dataset>::Quad<'a>>> + 'a>;
30/// Type alias for terms produced by a dataset.
31pub type DTerm<'a, D> = <<D as Dataset>::Quad<'a> as Quad>::Term;
32/// Type alias for fallible term iterators produced by a dataset.
33///
34/// See [`Dataset::subjects`] for more information about how to use it.
35pub type DTermSource<'a, D> = Box<dyn Iterator<Item = DResult<D, DTerm<'a, D>>> + 'a>;
36
37/// Generic trait for RDF datasets.
38///
39/// For convenience, this trait is implemented
40/// by [standard collections of quads](#foreign-impls).
41///
42/// NB: the semantics of this trait allows a dataset to contain duplicate quads;
43/// see also [`SetDataset`].
44pub trait Dataset {
45    /// Determine the type of [`Quad`]s
46    /// that the methods of this dataset will yield.
47    type Quad<'x>: Quad
48    where
49        Self: 'x;
50    /// The error type that this dataset may raise.
51    type Error: Error + 'static;
52
53    /// An iterator visiting all quads of this dataset in arbitrary order.
54    ///
55    /// This iterator is fallible:
56    /// its items are `Result`s,
57    /// an error may occur at any time during the iteration.
58    ///
59    /// # Examples
60    ///
61    /// The result of this method is an iterator,
62    /// so it can be used in a `for` loop:
63    /// ```
64    /// # fn test() -> Result<(), Box<dyn std::error::Error>> {
65    /// # use sophia_api::dataset::Dataset;
66    /// # use sophia_api::term::SimpleTerm;
67    /// # let dataset = Vec::<[SimpleTerm;4]>::new();
68    /// #
69    /// for q in dataset.quads() {
70    ///     let q = q?; // rethrow error if any
71    ///     // do something with q
72    /// }
73    /// #
74    /// # Ok(())
75    /// # }
76    /// ```
77    /// Another way is to use the specific methods provided by [`QuadSource`],
78    /// for example:
79    /// ```
80    /// # use sophia_api::dataset::Dataset;
81    /// # use sophia_api::term::SimpleTerm;
82    /// # use sophia_api::source::QuadSource;
83    /// # fn test() -> Result<(), Box<dyn std::error::Error>> {
84    /// # let dataset = Vec::<[SimpleTerm;4]>::new();
85    /// #
86    /// dataset.quads().for_each_quad(|q| {
87    ///     // do something with q
88    /// })?; // rethrow error if any
89    /// #
90    /// # Ok(())
91    /// # }
92    /// ```
93    fn quads(&self) -> DQuadSource<Self>;
94
95    /// An iterator visiting all quads matching the given subject, predicate and object.
96    /// See [`crate::term::matcher`]
97    ///
98    /// See also [`quads`](Dataset::quads).
99    ///
100    /// # Usage
101    ///
102    /// Typical implementations of [`TermMatcher`] include arrays/slices of [`Term`]s,
103    /// closure accepting a [`SimpleTerm`], or the special matcher [`Any`].
104    ///
105    /// [`Term`]: crate::term::Term
106    /// [`SimpleTerm`]: crate::term::SimpleTerm
107    /// [`Any`]: crate::term::matcher::Any
108    /// ```
109    /// # use sophia_api::prelude::*;
110    /// # use sophia_api::ns::{Namespace, rdf};
111    /// #
112    /// # fn test<G: Dataset>(dataset: &G) -> Result<(), Box<dyn std::error::Error>>
113    /// # where
114    /// #     G: Dataset,
115    /// # {
116    /// #
117    /// let s = Namespace::new("http://schema.org/")?;
118    /// let city = s.get("City")?;
119    /// let country = s.get("Country")?;
120    ///
121    /// for q in dataset.quads_matching(Any, [&rdf::type_], [city, country], Any) {
122    ///     println!("{:?} was found", q?.s());
123    /// }
124    /// #
125    /// # Ok(()) }
126    /// ```
127    ///
128    /// Here is another example using a closure as a [`TermMatcher`].
129    ///
130    /// ```
131    /// # use sophia_api::dataset::Dataset;
132    /// # use sophia_api::term::{SimpleTerm, Term};
133    /// # use sophia_api::quad::Quad;
134    /// # use sophia_api::ns::rdfs;
135    /// #
136    /// # fn test<G>(dataset: &G) -> Result<(), Box<dyn std::error::Error>>
137    /// # where
138    /// #     G: Dataset,
139    /// # {
140    /// #
141    /// use sophia_api::term::matcher::Any;
142    ///
143    /// for q in dataset.quads_matching(
144    ///     Any,
145    ///     [&rdfs::label],
146    ///     |t: SimpleTerm| t.lexical_form().map(|v| v.contains("needle")).unwrap_or(false),
147    ///     Any,
148    /// ) {
149    ///     println!("{:?} was found", q?.s());
150    /// }
151    /// #
152    /// # Ok(()) }
153    /// ```
154    fn quads_matching<'s, S, P, O, G>(&'s self, sm: S, pm: P, om: O, gm: G) -> DQuadSource<'s, Self>
155    where
156        S: TermMatcher + 's,
157        P: TermMatcher + 's,
158        O: TermMatcher + 's,
159        G: GraphNameMatcher + 's,
160    {
161        Box::new(self.quads().filter_ok(move |q| {
162            q.matched_by(
163                sm.matcher_ref(),
164                pm.matcher_ref(),
165                om.matcher_ref(),
166                gm.matcher_ref(),
167            )
168        }))
169    }
170
171    /// Return `true` if this dataset contains the given quad.
172    fn contains<TS, TP, TO, TG>(&self, s: TS, p: TP, o: TO, g: GraphName<TG>) -> DResult<Self, bool>
173    where
174        TS: Term,
175        TP: Term,
176        TO: Term,
177        TG: Term,
178    {
179        self.quads_matching([s], [p], [o], [g])
180            .next()
181            .transpose()
182            .map(|o| o.is_some())
183    }
184
185    /// Build a fallible iterator of all the terms used as subject in this Dataset.
186    ///
187    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
188    /// Users MUST therefore be prepared to deal with duplicates.
189    fn subjects(&self) -> DTermSource<Self> {
190        Box::new(self.quads().map_ok(Quad::to_s))
191    }
192
193    /// Build a fallible iterator of all the terms used as predicate in this Dataset.
194    ///
195    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
196    /// Users MUST therefore be prepared to deal with duplicates.
197    fn predicates(&self) -> DTermSource<Self> {
198        Box::new(self.quads().map_ok(Quad::to_p))
199    }
200
201    /// Build a fallible iterator of all the terms used as object in this Dataset.
202    ///
203    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
204    /// Users MUST therefore be prepared to deal with duplicates.
205    fn objects(&self) -> DTermSource<Self> {
206        Box::new(self.quads().map_ok(Quad::to_o))
207    }
208
209    /// Build a fallible iterator of all the terms used as graph name in this Dataset.
210    ///
211    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
212    /// Users MUST therefore be prepared to deal with duplicates.
213    fn graph_names(&self) -> DTermSource<Self> {
214        Box::new(self.quads().filter_map_ok(Quad::to_g))
215    }
216
217    /// Build a fallible iterator of all the IRIs used in this Dataset
218    /// (including those used inside quoted quads, if any).
219    ///
220    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
221    /// Users MUST therefore be prepared to deal with duplicates.
222    fn iris(&self) -> DTermSource<Self> {
223        Box::new(
224            self.quads()
225                .flat_map_ok(iter_spog)
226                .flat_map_ok(Term::to_atoms)
227                .filter_ok(Term::is_iri),
228        )
229    }
230
231    /// Build a fallible iterator of all the blank nodes used in this Dataset
232    /// (including those used inside quoted quads, if any).
233    ///
234    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
235    /// Users MUST therefore be prepared to deal with duplicates.
236    fn blank_nodes(&self) -> DTermSource<Self> {
237        Box::new(
238            self.quads()
239                .flat_map_ok(iter_spog)
240                .flat_map_ok(Term::to_atoms)
241                .filter_ok(Term::is_blank_node),
242        )
243    }
244
245    /// Build a fallible iterator of all the literals used in this Dataset
246    /// (including those used inside quoted quads, if any).
247    ///
248    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
249    /// Users MUST therefore be prepared to deal with duplicates.
250    fn literals(&self) -> DTermSource<Self> {
251        Box::new(
252            self.quads()
253                .flat_map_ok(iter_spog)
254                .flat_map_ok(Term::to_atoms)
255                .filter_ok(Term::is_literal),
256        )
257    }
258
259    /// Build a fallible iterator of all the quoted triples used in this Dataset
260    /// (including those used inside quoted triples, if any).
261    ///
262    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
263    /// Users MUST therefore be prepared to deal with duplicates.
264    fn quoted_triples<'s>(&'s self) -> DTermSource<'s, Self>
265    where
266        DTerm<'s, Self>: Clone,
267    {
268        Box::new(
269            self.quads()
270                .flat_map_ok(iter_spog)
271                .flat_map_ok(Term::to_constituents)
272                .filter_ok(Term::is_triple),
273        )
274    }
275
276    /// Build a fallible iterator of all the variables used in this Dataset
277    /// (including those used inside quoted quads, if any).
278    ///
279    /// NB: implementations SHOULD avoid yielding the same term multiple times, but MAY do so.
280    /// Users MUST therefore be prepared to deal with duplicates.
281    fn variables(&self) -> DTermSource<Self> {
282        Box::new(
283            self.quads()
284                .flat_map_ok(iter_spog)
285                .flat_map_ok(Term::to_atoms)
286                .filter_ok(Term::is_variable),
287        )
288    }
289
290    /// Borrows one of the graphs of this dataset
291    fn graph<T>(&self, graph_name: GraphName<T>) -> DatasetGraph<&Self, T>
292    where
293        T: for<'x> Term<BorrowTerm<'x> = DTerm<'x, Self>> + 'static,
294    {
295        DatasetGraph::new(self, graph_name)
296    }
297
298    /// Borrows mutably one of the graphs of this dataset
299    fn graph_mut<T>(&mut self, graph_name: GraphName<T>) -> DatasetGraph<&mut Self, T>
300    where
301        T: for<'x> Term<BorrowTerm<'x> = DTerm<'x, Self>> + 'static,
302    {
303        DatasetGraph::new(self, graph_name)
304    }
305
306    /// Borrows a graph that is the union of some of this dataset's graphs
307    fn partial_union_graph<M>(&self, selector: M) -> PartialUnionGraph<&Self, M>
308    where
309        M: GraphNameMatcher + Copy,
310    {
311        PartialUnionGraph::new(self, selector)
312    }
313
314    /// Borrows a graph that is the union of all this dataset's graphs (default and named)
315    fn union_graph(&self) -> UnionGraph<&Self> {
316        UnionGraph::new(self)
317    }
318
319    /// Convert into a graph that is the union of all this dataset's graphs (default and named)
320    fn into_union_graph(self) -> UnionGraph<Self>
321    where
322        Self: Sized,
323    {
324        UnionGraph::new(self)
325    }
326}
327
328/// A [`Dataset`] that can be constructed from a [`QuadSource`]
329pub trait CollectibleDataset: Dataset + Sized {
330    /// Construct a dataset from the given source
331    fn from_quad_source<TS: QuadSource>(quads: TS) -> StreamResult<Self, TS::Error, Self::Error>;
332}
333
334/// Type alias for results produced by a mutable dataset.
335pub type MdResult<D, T> = std::result::Result<T, <D as MutableDataset>::MutationError>;
336
337/// Generic trait for mutable RDF datasets.
338///
339/// NB: the semantics of this trait allows a dataset to contain duplicate quads;
340/// see also [`SetDataset`].
341pub trait MutableDataset: Dataset {
342    /// The error type that this dataset may raise during mutations.
343    type MutationError: Error + 'static;
344
345    /// Insert the given quad in this dataset.
346    ///
347    /// # Return value
348    /// The `bool` value returned in case of success is
349    /// **not significant unless** this dataset also implements [`SetDataset`].
350    ///
351    /// If it does,
352    /// `true` is returned iff the insertion actually changed the dataset.
353    /// In other words,
354    /// a return value of `false` means that the dataset was not changed,
355    /// because the quad was already present in this [`SetDataset`].
356    ///
357    /// See also [`MutableDataset::insert_quad`]
358    ///
359    /// # Usage
360    /// ```
361    /// # use sophia_api::dataset::{MutableDataset, MdResult};
362    /// # use sophia_api::ns::{Namespace, rdf, rdfs, xsd};
363    /// # use sophia_api::term::SimpleTerm;
364    /// # fn populate<D: MutableDataset>(dataset: &mut D) -> MdResult<D, ()> {
365    /// #
366    /// let schema = Namespace::new("http://schema.org/").unwrap();
367    /// let s_name = schema.get("name").unwrap();
368    /// let default_graph: Option<&'static SimpleTerm<'static>> = None;
369    ///
370    /// dataset.insert(&s_name, &rdf::type_, &rdf::Property, default_graph)?;
371    /// dataset.insert(&s_name, &rdfs::range, &xsd::string, default_graph)?;
372    /// dataset.insert(&s_name, &rdfs::comment, "The name of the item.", Some(&rdfs::comment))?;
373    /// #
374    /// # Ok(())
375    /// # }
376    /// ```
377    fn insert<TS, TP, TO, TG>(
378        &mut self,
379        s: TS,
380        p: TP,
381        o: TO,
382        g: GraphName<TG>,
383    ) -> MdResult<Self, bool>
384    where
385        TS: Term,
386        TP: Term,
387        TO: Term,
388        TG: Term;
389
390    /// Insert in this graph the given quad.
391    ///
392    /// NB: if you want to insert a quad `q` while keeping its ownership,
393    /// you can still pass [`q.spog()`](Quad::spog).
394    ///
395    /// See also [`MutableDataset::insert`]
396    fn insert_quad<T>(&mut self, quad: T) -> MdResult<Self, bool>
397    where
398        T: Quad,
399    {
400        let ([s, p, o], g) = quad.to_spog();
401        self.insert(s, p, o, g)
402    }
403
404    /// Remove the given quad from this dataset.
405    ///
406    /// # Return value
407    /// The `bool` value returned in case of success is
408    /// **not significant unless** this dataset also implements [`SetDataset`].
409    ///
410    /// If it does,
411    /// `true` is returned iff the removal actually changed the dataset.
412    /// In other words,
413    /// a return value of `false` means that the dataset was not changed,
414    /// because the quad was already absent from this [`SetDataset`].
415    fn remove<TS, TP, TO, TG>(
416        &mut self,
417        s: TS,
418        p: TP,
419        o: TO,
420        g: GraphName<TG>,
421    ) -> MdResult<Self, bool>
422    where
423        TS: Term,
424        TP: Term,
425        TO: Term,
426        TG: Term;
427
428    /// Remove from this graph a the given quad.
429    ///
430    /// NB: if you want to remove a quad `q` while keeping its ownership,
431    /// you can still pass [`q.spog()`](Quad::spog).
432    ///
433    /// See also [MutableDataset::remove]
434    fn remove_quad<T>(&mut self, quad: T) -> MdResult<Self, bool>
435    where
436        T: Quad,
437    {
438        let ([s, p, o], g) = quad.to_spog();
439        self.remove(s, p, o, g)
440    }
441
442    /// Insert into this dataset all quads from the given source.
443    ///
444    /// # Blank node scope
445    /// The blank nodes contained in the quad source will be inserted as is.
446    /// If they happen to have the same identifier as blank nodes already present,
447    /// they will be considered equal.
448    /// This might *not* be what you want,
449    /// especially if the dataset contains data from a file,
450    /// and you are inserting data from a different file.
451    /// In that case, you should first transform the quad source,
452    /// in order to get fresh blank node identifiers.
453    ///
454    /// # Return value
455    /// The `usize` value returned in case of success is
456    /// **not significant unless** this dataset also implements [`SetDataset`].
457    ///
458    /// If it does,
459    /// the number of quads that were *actually* inserted
460    /// (i.e. that were not already present in this [`SetDataset`])
461    /// is returned.
462    #[inline]
463    fn insert_all<TS: QuadSource>(
464        &mut self,
465        src: TS,
466    ) -> StreamResult<usize, TS::Error, <Self as MutableDataset>::MutationError> {
467        let mut src = src;
468        let mut c = 0;
469        src.try_for_each_quad(|q| -> MdResult<Self, ()> {
470            if self.insert_quad(q.spog())? {
471                c += 1;
472            }
473            Ok(())
474        })
475        .and(Ok(c))
476    }
477
478    /// Remove from this dataset all quads from the given source.
479    ///
480    /// # Return value
481    /// The `usize` value returned in case of success is
482    /// **not significant unless** this dataset also implements [`SetDataset`].
483    ///
484    /// If it does,
485    /// the number of quads that were *actually* removed
486    /// (i.e. that were not already absent from this [`SetDataset`])
487    /// is returned.
488    #[inline]
489    fn remove_all<TS: QuadSource>(
490        &mut self,
491        src: TS,
492    ) -> StreamResult<usize, TS::Error, <Self as MutableDataset>::MutationError> {
493        let mut src = src;
494        let mut c = 0;
495        src.try_for_each_quad(|q| -> MdResult<Self, ()> {
496            if self.remove_quad(q.spog())? {
497                c += 1;
498            }
499            Ok(())
500        })
501        .and(Ok(c))
502    }
503
504    /// Remove all quads matching the given matchers.
505    ///
506    /// # Return value
507    /// The `usize` value returned in case of success is
508    /// **not significant unless** this dataset also implements [`SetDataset`].
509    ///
510    /// If it does,
511    /// the number of quads that were *actually* removed
512    /// (i.e. that were not already absent from this [`SetDataset`])
513    /// is returned.
514    ///
515    /// # Note to implementors
516    /// The default implementation is rather naive,
517    /// and could be improved in specific implementations of the trait.
518    fn remove_matching<S, P, O, G>(
519        &mut self,
520        ms: S,
521        mp: P,
522        mo: O,
523        mg: G,
524    ) -> Result<usize, Self::MutationError>
525    where
526        S: TermMatcher,
527        P: TermMatcher,
528        O: TermMatcher,
529        G: GraphNameMatcher,
530        Self::MutationError: From<Self::Error>,
531    {
532        let to_remove: Result<Vec<([SimpleTerm; 3], GraphName<SimpleTerm>)>, _> = self
533            .quads_matching(ms, mp, mo, mg)
534            .map_ok(|q| {
535                let (spo, g) = q.spog();
536                (spo.map(Term::into_term), g.map(Term::into_term))
537            })
538            .collect();
539        self.remove_all(to_remove?.into_iter().into_quad_source())
540            .map_err(|err| err.unwrap_sink_error())
541    }
542
543    /// Keep only the quads matching the given matchers.
544    ///
545    /// # Note to implementors
546    /// The default implementation is rather naive,
547    /// and could be improved in specific implementations of the trait.
548    fn retain_matching<S, P, O, G>(
549        &mut self,
550        ms: S,
551        mp: P,
552        mo: O,
553        mg: G,
554    ) -> Result<(), Self::MutationError>
555    where
556        S: TermMatcher,
557        P: TermMatcher,
558        O: TermMatcher,
559        G: GraphNameMatcher,
560        Self::MutationError: From<Self::Error>,
561    {
562        let to_remove: Result<Vec<([SimpleTerm; 3], GraphName<SimpleTerm>)>, _> = self
563            .quads()
564            .filter_ok(|q| {
565                !q.matched_by(
566                    ms.matcher_ref(),
567                    mp.matcher_ref(),
568                    mo.matcher_ref(),
569                    mg.matcher_ref(),
570                )
571            })
572            .map_ok(|q| {
573                let (spo, g) = q.spog();
574                (spo.map(Term::into_term), g.map(Term::into_term))
575            })
576            .collect();
577        self.remove_all(to_remove?.into_iter().into_quad_source())
578            .map_err(|err| err.unwrap_sink_error())?;
579        Ok(())
580    }
581}
582
583/// Marker trait constraining the semantics of
584/// [`Dataset`] and [`MutableDataset`].
585///
586/// It guarantees that
587/// (1) quads will never be returned / stored multiple times.
588///
589/// If the type also implements [`MutableDataset`],
590/// it must also ensure that
591/// (2) the `bool` or `usize` values returned by [`MutableDataset`]
592/// methods accurately describe how many quads were actually added/removed.
593///
594/// # Note to implementors
595/// A type implementing both [`Dataset`] and [`MutableDataset`],
596/// enforcing (1) but failing to enforce (2)
597/// *must not* implement this trait.
598pub trait SetDataset: Dataset {}
599
600mod check_implementability {
601    /// This is a naive implementation of an RDF-star dataset,
602    /// where the dataset maintains
603    /// - a list of terms (either atoms or index of quad)
604    /// - a list of triples (SPO indexes)
605    /// - a list of named graphs associated the triple indexes contained in the graph
606    /// This avoids the need to store arbitrarily nested triples.
607    /// NB: unasserted triples are not used in any quoted graph.
608    use super::*;
609    use crate::term::SimpleTerm;
610    use std::collections::HashMap;
611
612    #[derive(Clone, Debug, Eq, PartialEq)]
613    #[allow(dead_code)] // testing implementability
614    enum MyInternalTerm {
615        Atom(SimpleTerm<'static>),
616        QuotedTriple(usize),
617    }
618    use MyInternalTerm::*;
619
620    #[derive(Clone, Debug)]
621    struct MyDataset {
622        terms: Vec<MyInternalTerm>,
623        triples: Vec<[usize; 3]>,
624        graphs: HashMap<usize, Vec<usize>>,
625    }
626
627    impl MyDataset {
628        fn make_term(&self, i: usize) -> SimpleTerm<'_> {
629            match &self.terms[i] {
630                Atom(t) => t.as_simple(),
631                QuotedTriple(j) => {
632                    SimpleTerm::Triple(Box::new(self.triples[*j].map(|k| self.make_term(k))))
633                }
634            }
635        }
636    }
637
638    impl Dataset for MyDataset {
639        type Quad<'x> = [SimpleTerm<'x>; 4] where Self: 'x;
640        type Error = std::convert::Infallible;
641
642        fn quads(&self) -> DQuadSource<Self> {
643            Box::new(self.graphs.iter().flat_map(move |(gi, tis)| {
644                let g = self.make_term(*gi);
645                tis.iter().copied().map(move |ti| {
646                    let [s, p, o] = self.triples[ti].map(|j| self.make_term(j));
647                    Ok([s, p, o, g.clone()])
648                })
649            }))
650        }
651    }
652}
653
654#[cfg(test)]
655mod check_implementability_lazy_term {
656    /// This implementation is internally similar to the one above,
657    /// but using dedicated lazy implementations of Term
658    /// (lazy because it avoids allocating nested triples until forced)
659    use super::*;
660    use crate::term::{SimpleTerm, TermKind};
661    use std::collections::HashMap;
662
663    #[derive(Clone, Debug, Eq, PartialEq)]
664    #[allow(dead_code)] // testing implementability
665    enum MyInternalTerm {
666        Atom(SimpleTerm<'static>),
667        QuotedTriple(usize),
668    }
669    use MyInternalTerm::*;
670
671    #[derive(Clone, Debug, Eq, PartialEq)]
672    struct MyInternalTriple {
673        asserted: bool,
674        spog: [usize; 3],
675    }
676
677    #[derive(Clone, Debug)]
678    struct MyDataset {
679        terms: Vec<MyInternalTerm>,
680        triples: Vec<[usize; 3]>,
681        graphs: HashMap<usize, Vec<usize>>,
682    }
683
684    #[derive(Clone, Copy, Debug)]
685    struct MyTerm<'a> {
686        dataset: &'a MyDataset,
687        index: usize,
688    }
689
690    impl<'a> Term for MyTerm<'a> {
691        type BorrowTerm<'x> = MyTerm<'x> where Self: 'x;
692
693        fn kind(&self) -> crate::term::TermKind {
694            if let Atom(t) = &self.dataset.terms[self.index] {
695                t.kind()
696            } else {
697                TermKind::Triple
698            }
699        }
700
701        fn iri(&self) -> Option<crate::term::IriRef<mownstr::MownStr>> {
702            if let Atom(t) = &self.dataset.terms[self.index] {
703                t.iri()
704            } else {
705                None
706            }
707        }
708
709        fn bnode_id(&self) -> Option<crate::term::BnodeId<mownstr::MownStr>> {
710            if let Atom(t) = &self.dataset.terms[self.index] {
711                t.bnode_id()
712            } else {
713                None
714            }
715        }
716
717        fn lexical_form(&self) -> Option<mownstr::MownStr> {
718            if let Atom(t) = &self.dataset.terms[self.index] {
719                t.lexical_form()
720            } else {
721                None
722            }
723        }
724
725        fn datatype(&self) -> Option<crate::term::IriRef<mownstr::MownStr>> {
726            if let Atom(t) = &self.dataset.terms[self.index] {
727                t.datatype()
728            } else {
729                None
730            }
731        }
732
733        fn language_tag(&self) -> Option<crate::term::LanguageTag<mownstr::MownStr>> {
734            if let Atom(t) = &self.dataset.terms[self.index] {
735                t.language_tag()
736            } else {
737                None
738            }
739        }
740
741        fn variable(&self) -> Option<crate::term::VarName<mownstr::MownStr>> {
742            if let Atom(t) = &self.dataset.terms[self.index] {
743                t.variable()
744            } else {
745                None
746            }
747        }
748
749        fn triple(&self) -> Option<[Self::BorrowTerm<'_>; 3]> {
750            self.to_triple()
751        }
752
753        fn to_triple(self) -> Option<[Self; 3]> {
754            if let QuotedTriple(i) = &self.dataset.terms[self.index] {
755                Some(self.dataset.triples[*i].map(|t| MyTerm {
756                    dataset: self.dataset,
757                    index: t,
758                }))
759            } else {
760                None
761            }
762        }
763
764        fn borrow_term(&self) -> Self::BorrowTerm<'_> {
765            *self
766        }
767    }
768
769    impl Dataset for MyDataset {
770        type Quad<'x> = [MyTerm<'x>; 4] where Self: 'x;
771        type Error = std::convert::Infallible;
772
773        fn quads(&self) -> DQuadSource<Self> {
774            Box::new(self.graphs.iter().flat_map(move |(gi, tis)| {
775                let g = MyTerm {
776                    dataset: self,
777                    index: *gi,
778                };
779                tis.iter().copied().map(move |ti| {
780                    let [s, p, o] = self.triples[ti].map(|j| MyTerm {
781                        dataset: self,
782                        index: j,
783                    });
784                    Ok([s, p, o, g])
785                })
786            }))
787        }
788    }
789}