inc_complete/storage/
macros.rs

1/// Helper macro to define an intermediate computation type.
2/// This will implement `OutputType`, `ComputationId`, and `Run`.
3///
4/// This macro supports multiple `Storage` types used, separated by `|`,
5/// in case your program uses multiple databases with differing storage types.
6///
7/// Signature:
8/// `define_intermediate!(computation_id, ComputationType -> OutputType, StorageType ( | MoreStorageTypes)*, run_function)`
9///
10/// Example usage:
11/// ```
12/// # use inc_complete::{ define_intermediate, define_input, storage::SingletonStorage, impl_storage, DbHandle };
13/// # struct MyStorageType { input: SingletonStorage<MyInput>, double: SingletonStorage<Double> }
14/// # #[derive(Clone)]
15/// # struct MyInput;
16/// # define_input!(0, MyInput -> i32, MyStorageType);
17/// # impl_storage!(MyStorageType, input:MyInput, double:Double);
18/// ##[derive(Clone)]
19/// struct Double;
20///
21/// // Define `Double` as a computation with id 1 and the given run function which returns an `i32`
22/// // to be used with a `Db<MyStorageType>` or `DbHandle<MyStorageType>`.
23/// // The type annotations on the closure are unnecessary.
24/// // We also may provide an existing function instead of a closure.
25/// define_intermediate!(1, Double -> i32, MyStorageType, |_: &Double, db: &DbHandle<MyStorageType>| {
26///     db.get(MyInput) * 2
27/// });
28/// ```
29#[cfg(not(feature = "async"))]
30#[macro_export]
31macro_rules! define_intermediate {
32    ( $id:tt, $type_name:ident -> $output_type:ty, $( $storage_type:ty )|+, $run_function:expr) => {
33        impl $crate::OutputType for $type_name {
34            type Output = $output_type;
35        }
36
37        impl $crate::ComputationId for $type_name {
38            fn computation_id() -> u32 {
39                $id
40            }
41        }
42
43        impl $type_name {
44            #[allow(unused)]
45            pub fn get(self, db: &impl $crate::DbGet<$type_name>) -> $output_type {
46                db.get(self)
47            }
48        }
49
50        $(
51        impl $crate::Run<$storage_type> for $type_name {
52            fn run(&self, db: &$crate::DbHandle<$storage_type>) -> $output_type {
53                // The type annotation here makes it so that users don't have to annotate
54                // the arguments of `run_function`.
55                let f: fn(&Self, &$crate::DbHandle<$storage_type>) -> $output_type =
56                    $run_function;
57                f(self, db)
58            }
59        }
60        )+
61    };
62}
63
64#[cfg(feature = "async")]
65#[macro_export]
66macro_rules! define_intermediate {
67    ( $id:tt, $type_name:ident -> $output_type:ty, $( $storage_type:ty )|+, $run_function:expr) => {
68        impl $crate::OutputType for $type_name {
69            type Output = $output_type;
70        }
71
72        impl $crate::ComputationId for $type_name {
73            fn computation_id() -> u32 {
74                $id
75            }
76        }
77
78        impl $type_name {
79            #[allow(unused)]
80            pub fn get(self, db: &impl $crate::DbGet<$type_name>) -> impl Future<Output = $output_type> + Send {
81                db.get(self)
82            }
83        }
84
85        $(
86        impl $crate::Run<$storage_type> for $type_name {
87            fn run<'db>(&self, db: &$crate::DbHandle<'db, $storage_type>) -> impl Future<Output = $output_type> {
88                $run_function(self, db)
89            }
90        }
91        )+
92    };
93}
94
95/// Helper macro to define an input computation type.
96/// This will implement `OutputType`, `ComputationId`, and `Run`.
97/// Note that the `Run` implementation will panic by default with a message that
98/// `update_input` should have been called beforehand.
99///
100/// This macro supports multiple `Storage` types used, separated by `|`,
101/// in case your program uses multiple databases with differing storage types.
102///
103/// Signature:
104/// `define_input!(computation_id, ComputationType -> OutputType, StorageType ( | MoreStorageTypes)* )`
105///
106/// Example usage:
107/// ```
108/// # use inc_complete::{ define_intermediate, define_input, storage::SingletonStorage, impl_storage, DbHandle };
109/// # struct MyStorageType { input: SingletonStorage<MyInput>, double: SingletonStorage<Double> }
110/// # impl_storage!(MyStorageType, input:MyInput,double:Double);
111/// # #[derive(Clone)]
112/// # struct Double;
113/// # define_intermediate!(1, Double -> i32, MyStorageType, |_: &Double, db: &DbHandle<MyStorageType>| {
114/// #     db.get(MyInput) * 2
115/// # });
116/// ##[derive(Clone)]
117/// struct MyInput;
118///
119/// // Define `MyInput` as an input computation with id 0 and an `i32` value
120/// // which can be used with a `Db<MyStorageType>`.
121/// define_input!(0, MyInput -> i32, MyStorageType);
122/// ```
123#[macro_export]
124macro_rules! define_input {
125    ( $id:tt, $type_name:ident -> $output_type:ty, $( $storage_type:ty )|+ ) => {
126        impl $crate::OutputType for $type_name {
127            type Output = $output_type;
128        }
129
130        impl $crate::ComputationId for $type_name {
131            fn computation_id() -> u32 {
132                $id
133            }
134        }
135
136        impl $type_name {
137            #[allow(unused)]
138            pub fn get(self, db: &impl $crate::DbGet<$type_name>) -> $output_type {
139                db.get(self)
140            }
141
142            #[allow(unused)]
143            pub fn set<S>(self, db: &mut $crate::Db<S>, value: $output_type) where S: $crate::Storage + $crate::StorageFor<$type_name> {
144                db.update_input(self, value);
145            }
146        }
147
148        $(
149        impl $crate::Run<$storage_type> for $type_name {
150            fn run(&self, _: &$crate::DbHandle<$storage_type>) -> $output_type {
151                panic!("Attempted to call `run` function on input {}, did you forget to call `update_input`?",
152                    stringify!($type_name))
153            }
154        }
155        )+
156    };
157}
158
159/// Implements `Storage` for a struct type. This enables the given struct type `S` to be used
160/// as a generic on `Db<S>` to store _all_ computations cached by the program.
161///
162/// This will also create forwarding impls for `StorageFor<ComputationType>` for each field,
163/// computation type pair used.
164///
165/// Example usage:
166/// ```
167/// use inc_complete::{ impl_storage, define_input, define_intermediate };
168/// use inc_complete::storage::{ SingletonStorage, HashMapStorage };
169///
170/// ##[derive(Default)]
171/// struct MyStorage {
172///     foos: SingletonStorage<Foo>,
173///     bars: HashMapStorage<Bar>,
174/// }
175///
176/// impl_storage!(MyStorage,
177///     foos: Foo,
178///     bars: Bar,
179/// );
180///
181/// // Each input & intermediate computation should implement Clone
182/// ##[derive(Clone)]
183/// struct Foo;
184/// define_input!(0, Foo -> usize, MyStorage);
185///
186/// // HashMapStorage requires Eq and Hash
187/// ##[derive(Clone, PartialEq, Eq, Hash)]
188/// struct Bar(std::rc::Rc<String>);
189/// define_intermediate!(1, Bar -> usize, MyStorage, |bar, db| {
190///     bar.0.len() + db.get(Foo)
191/// });
192/// ```
193///
194/// Note that using this macro requires each computation type to implement `Clone`.
195#[macro_export]
196macro_rules! impl_storage {
197    ($typ:ty, $( $field:ident : $computation_type:ty ),* $(, )? ) => {
198        impl $crate::Storage for $typ {
199            fn output_is_unset(&self, cell: $crate::Cell, computation_id: u32) -> bool {
200                use $crate::StorageFor;
201                match computation_id {
202                    $(
203                        x if x == <$computation_type as $crate::ComputationId>::computation_id() => {
204                            self.$field.get_output(cell).is_none()
205                        },
206                    )*
207                    id => panic!("Unknown computation id: {id}"),
208                }
209            }
210
211            $crate::run_computation!( $($field: $computation_type),* );
212        }
213
214        $(
215        impl $crate::StorageFor<$computation_type> for $typ {
216            fn get_cell_for_computation(&self, key: &$computation_type) -> Option<$crate::Cell> {
217                self.$field.get_cell_for_computation(key)
218            }
219
220            fn insert_new_cell(&self, cell: $crate::Cell, key: $computation_type) {
221                self.$field.insert_new_cell(cell, key)
222            }
223
224            fn get_input(&self, cell: $crate::Cell) -> $computation_type {
225                self.$field.get_input(cell)
226            }
227
228            fn get_output(&self, cell: $crate::Cell) -> Option<<$computation_type as $crate::OutputType>::Output> {
229                self.$field.get_output(cell)
230            }
231
232            fn update_output(&self, cell: $crate::Cell, new_value: <$computation_type as $crate::OutputType>::Output) -> bool {
233                self.$field.update_output(cell, new_value)
234            }
235        })*
236    };
237}
238
239#[cfg(not(feature = "async"))]
240#[doc(hidden)]
241#[macro_export]
242macro_rules! run_computation {
243    ( $($field:ident: $computation_type:ty),* ) => {
244        fn run_computation(db: &$crate::DbHandle<Self>, cell: $crate::Cell, computation_id: u32) -> bool {
245            use $crate::{ StorageFor, Run };
246            match computation_id {
247                $(
248                    x if x == <$computation_type as $crate::ComputationId>::computation_id() => {
249                        let new_value = db.storage().$field.get_input(cell).run(db);
250                        db.storage().$field.update_output(cell, new_value)
251                    }
252                )*
253                id => panic!("Unknown computation id: {id}"),
254            }
255        }
256    }
257}
258
259#[cfg(feature = "async")]
260#[doc(hidden)]
261#[macro_export]
262macro_rules! run_computation {
263    ( $($field:ident: $computation_type:ty),* ) => {
264        async fn run_computation<'db>(db: &$crate::DbHandle<'db, Self>, cell: $crate::Cell, computation_id: u32) -> bool {
265            use $crate::{ StorageFor, Run };
266            match computation_id {
267                $(
268                    x if x == <$computation_type as $crate::ComputationId>::computation_id() => {
269                        let new_value = db.storage().$field.get_input(cell).run(db).await;
270                        db.storage().$field.update_output(cell, new_value)
271                    }
272                )*
273                id => panic!("Unknown computation id: {id}"),
274            }
275        }
276    }
277}