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