1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
//! Traits and implementations for working with index members.
//!
//! There are 3 ways to interact with an index field:
//!
//! - [`Store`]: Store the field into the index.
//! - [`Query`]: Query the field and load selected values into memory.
//! - [`Load`]: Load all contents of the field into memory.
//!
//! To implement how a field is actually stored in the index, we
//! define an access [`Strategy`]. Currently 2 access strategies are
//! implemented in Infinitree, but the storage system is extensible.
//!
//! - [`SparseField`]: Store the key in the index, but the value in
//! the object store
//! - [`LocalField`]: Store both the key and the value in the index.
//!
//! Additionally, `infinitree` can work with "snapshot" or
//! "incremental" fields. This [`depth`] of the field will determine its
//! behaviour during [`Load`] or [`Query`] operation.
//!
//! This is a detail that you need to be aware
//! of when designing your indexes, but the implementation details are
//! only relevant if you intend to write your own field type.
//!
//! - [`Incremental`](depth::Incremental): The entire
//! commit list will be traversed, typically useful for incremental collection types.
//! - [`Snapshot`](depth::Snapshot): Only the last commit is visited,
//! restoring a point-in-time snapshot of the contents.
//!
//! To learn more about index internals, see the module documentation
//! in the [`index`](super) module.
use crate::{
index::{FieldReader, TransactionList},
object::{self, AEADReader, Pool},
};
use serde::{de::DeserializeOwned, Serialize};
use std::{cmp::Eq, hash::Hash, sync::Arc};
/// Marker trait for values that can be serialized and used as a
/// value for an index field
///
/// You should generally not implement this trait as a blanket
/// implementation will cover all types that conform.
pub trait Value: Serialize + DeserializeOwned + Send + Sync {}
/// Marker trait for value that can be used as a key in an index
///
/// You should generally not implement this trait as a blanket
/// implementation will cover all types that conform.
pub trait Key: Serialize + DeserializeOwned + Eq + Hash + Send + Sync {}
impl<T> Value for T where T: Serialize + DeserializeOwned + Send + Sync {}
impl<T> Key for T where T: Serialize + DeserializeOwned + Eq + Hash + Send + Sync {}
mod map;
pub use map::Map;
mod list;
pub use list::List;
// mod set;
// pub use set::Set;
mod query;
pub use query::*;
mod serialized;
pub use serialized::Serialized;
mod versioned;
pub use versioned::list::LinkedList;
pub use versioned::map::VersionedMap;
pub mod depth;
use depth::Depth;
pub mod strategy;
#[allow(unused)]
pub(crate) use strategy::Strategy;
pub use strategy::{LocalField, SparseField};
pub mod intent;
pub use intent::{Intent, Load, Query, Store};
/// Query an index field, but do not automatically load it into memory
///
/// To allow lazily loading data from e.g. a [`SparseField`] when
/// relevant, a predicate is taken that controls the iterator.
///
/// This trait should be implemented on a type that also implements
/// [`Strategy`], and _not_ on the field directly.
pub trait Collection {
/// Use this strategy to load the collection.
///
/// Typically this will be one of two types:
///
/// * `Incremental` if a collection requires
/// crawling the full transaction history for an accurate
/// representation after loading.
/// * `Snapshot` if the collection is not versioned and
/// therefore there's no need to resolve the full the
/// transaction list.
type Depth: Depth;
/// The key that the predicate will use to decide whether to pull
/// more data into memory.
type Key;
/// The serialized record format. This type will typically
/// implement [`serde::Serialize`]
type Serialized: DeserializeOwned;
/// This is equivalent to `Iterator::Item`, and should contain a
/// full record that can be inserted into the in-memory store.
type Item;
/// Get the key based on the deserialized data. You want this to
/// be a reference that's easy to derive from the serialized data.
fn key(from: &Self::Serialized) -> &Self::Key;
/// Load the full record, and return it
fn load(from: Self::Serialized, object: &mut dyn object::Reader) -> Self::Item;
/// Store the deserialized record in the collection
fn insert(&mut self, record: Self::Item);
}
impl<T> Query for T
where
T: Collection,
{
type Key = T::Key;
fn select(
&mut self,
pool: Pool<AEADReader>,
transaction_list: TransactionList,
predicate: impl Fn(&Self::Key) -> QueryAction,
) {
let predicate = Arc::new(predicate);
let mut reader = pool.lease().unwrap();
for transaction in T::Depth::resolve(pool, transaction_list) {
let iter = QueryIterator::new(transaction, &mut reader, predicate.clone(), self);
for item in iter {
self.insert(item);
}
}
}
}
impl<T: Collection> Collection for LocalField<T> {
type Depth = T::Depth;
type Key = T::Key;
type Serialized = T::Serialized;
type Item = T::Item;
fn key(from: &Self::Serialized) -> &Self::Key {
T::key(from)
}
fn load(from: Self::Serialized, object: &mut dyn object::Reader) -> Self::Item {
T::load(from, object)
}
fn insert(&mut self, record: Self::Item) {
self.field.insert(record)
}
}