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