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