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}