triblespace_core/trible/spread.rs
1use crate::id::Id;
2use crate::id::RawId;
3use crate::patch::{IdentitySchema, PATCHIntoOrderedIterator};
4
5use super::Fragment;
6
7/// Trait for types that can be "spread" into an `entity!` repeated attribute.
8///
9/// A spread produces an iterator of attribute values, plus a Fragment
10/// of extras (facts + blobs) that gets merged into the entity's result
11/// fragment.
12///
13/// Plain iterators return an empty extras Fragment. A [`Fragment`] returns
14/// its exported ids as the values and its contained facts + blobs as the
15/// extras.
16pub trait Spread {
17 /// The type of each yielded value.
18 type Item;
19 /// The iterator type returned by [`spread`](Spread::spread).
20 type Iter: IntoIterator<Item = Self::Item>;
21 /// Decomposes the value into an iterator of items and extras (facts +
22 /// blobs) to merge.
23 fn spread(self) -> (Self::Iter, Fragment);
24}
25
26impl<I: IntoIterator> Spread for I {
27 type Item = I::Item;
28 type Iter = I;
29 fn spread(self) -> (Self::Iter, Fragment) {
30 (self, Fragment::empty())
31 }
32}
33
34/// Free function (not a closure) so `Map`'s type is nameable in
35/// `Spread::Iter` below — keeps `Fragment::spread` allocation-free.
36fn raw_to_id(raw: RawId) -> Id {
37 Id::new(raw).expect("export ids are non-nil")
38}
39
40impl Spread for Fragment {
41 type Item = Id;
42 type Iter = std::iter::Map<
43 PATCHIntoOrderedIterator<16, IdentitySchema, ()>,
44 fn(RawId) -> Id,
45 >;
46 fn spread(self) -> (Self::Iter, Fragment) {
47 let (exports, facts, blobs) = self.into_parts();
48 // Wrap the remaining facts + blobs as an extras fragment with
49 // no exports — the exports are consumed lazily as the spread
50 // values via the mapping iterator below.
51 let extras = Fragment::from_facts_and_blobs(facts, blobs);
52 let iter = exports.into_iter_ordered().map(raw_to_id as fn(_) -> _);
53 (iter, extras)
54 }
55}