sophia_api/
graph.rs

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