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}