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
//! Intent to execute some operation on an [`Index`](crate::Index) field

use super::{query::QueryAction, LocalField};
use crate::{
    index::{Transaction, TransactionList},
    object::{self, AEADReader, Pool},
};

/// A wrapper to allow working with trait objects and `impl Trait`
/// types when accessing the index field.
#[non_exhaustive]
#[derive(Clone)]
pub struct Intent<T> {
    /// The stringy name of the field that's being accessed. This MUST
    /// be unique within the index.
    pub name: String,

    /// The strategy for the given access that's to be executed.
    pub strategy: T,
}

impl<T> Intent<T> {
    /// Create a new wrapper that binds a stringy field name to an
    /// access strategy
    #[inline(always)]
    pub fn new(name: impl AsRef<str>, strategy: T) -> Self {
        Intent {
            name: name.as_ref().to_string(),
            strategy,
        }
    }
}

impl<T: Store + 'static> From<Intent<Box<T>>> for Intent<Box<dyn Store>> {
    #[inline(always)]
    fn from(a: Intent<Box<T>>) -> Self {
        Intent {
            name: a.name,
            strategy: a.strategy,
        }
    }
}

impl<T: Load + 'static> From<Intent<Box<T>>> for Intent<Box<dyn Load>> {
    #[inline(always)]
    fn from(a: Intent<Box<T>>) -> Self {
        Intent {
            name: a.name,
            strategy: a.strategy,
        }
    }
}

/// Store data into the index.
///
/// This trait is usually implemented on a type that also implements
/// [`Strategy`](super::strategy::Strategy), and _not_ on the field directly.
pub trait Store {
    /// Store the contents of the field into the index. The field
    /// itself needs to track whether this should be a complete
    /// rewrite or an upsert.
    ///
    /// The `transaction` parameter is provided for strategies to
    /// store values in the index, while the `object` is to store
    /// values in the object pool.
    ///
    /// Typically, the [`ChunkPointer`][crate::ChunkPointer] values returned by `object`
    /// should be stored in the index.
    fn store(&mut self, transaction: &mut dyn Transaction, object: &mut dyn object::Writer);
}

impl<T: Store> Store for LocalField<T> {
    fn store(&mut self, transaction: &mut dyn Transaction, object: &mut dyn object::Writer) {
        self.field.store(transaction, object)
    }
}

/// Load all data from the index field into memory.
///
/// This trait is usually implemented on a type that also implements
/// [`Strategy`](super::strategy::Strategy), and _not_ on the field directly.
///
/// In addition, `Load` has a blanket implementation for all types
/// that implement [`Query`], so very likely you never have to
/// manually implement this yourself.
pub trait Load {
    /// Execute a load action.
    ///
    /// The `index` and `object` readers are provided to interact with
    /// the indexes and the object pool, respectively.
    ///
    /// `transaction_list` can contain any list of transactions that
    /// this loader should restore into memory.
    ///
    /// Note that this is decidedly not a type safe way to interact
    /// with a collection, and therefore it is recommended that
    /// `transaction_list` is prepared and sanitized for the field
    /// that's being restored.
    fn load(&mut self, pool: Pool<AEADReader>, transaction_list: TransactionList);
}

impl<K, T> Load for T
where
    T: Query<Key = K>,
{
    #[inline(always)]
    fn load(&mut self, pool: Pool<AEADReader>, transaction_list: TransactionList) {
        Query::select(self, pool, transaction_list, |_| QueryAction::Take)
    }
}

/// Load data into memory where a predicate indicates it's needed
///
/// This trait should be implemented on a type that also implements
/// [`Strategy`](super::strategy::Strategy), and _not_ on the field directly.
pub trait Query {
    /// The key that the predicate will use to decide whether to pull
    /// more data into memory.
    type Key;

    /// Load items into memory based on a predicate
    ///
    /// The `index` and `object` readers are provided to interact with
    /// the indexes and the object pool, respectively.
    ///
    /// `transaction_list` can contain any list of transactions that
    /// this loader should restore into memory.
    ///
    /// Note that this is decidedly not a type safe way to interact
    /// with a collection, and therefore it is recommended that
    /// `transaction_list` is prepared and sanitized for the field
    /// that's being restored.
    fn select(
        &mut self,
        pool: Pool<AEADReader>,
        transaction_list: TransactionList,
        predicate: impl Fn(&Self::Key) -> QueryAction,
    );
}