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: &mut DbHandle<MyStorageType>| {
26///     *db.get(MyInput) * 2
27/// });
28/// ```
29#[macro_export]
30macro_rules! define_intermediate {
31    // Without the `->`, rustfmt has really bad formatting when calling this macro
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        $(
44        impl $crate::Run<$storage_type> for $type_name {
45            fn run(&self, db: &mut $crate::DbHandle<$storage_type>) -> $output_type {
46                // The type annotation here makes it so that users don't have to annotate
47                // the arguments of `run_function`.
48                let f: fn(&Self, &mut $crate::DbHandle<$storage_type>) -> $output_type =
49                    $run_function;
50                f(self, db)
51            }
52        }
53        )+
54    };
55}
56
57/// Helper macro to define an input computation type.
58/// This will implement `OutputType`, `ComputationId`, and `Run`.
59/// Note that the `Run` implementation will panic by default with a message that
60/// `update_input` should have been called beforehand.
61///
62/// This macro supports multiple `Storage` types used, separated by `|`,
63/// in case your program uses multiple databases with differing storage types.
64///
65/// Signature:
66/// `define_input!(computation_id, ComputationType -> OutputType, StorageType ( | MoreStorageTypes)* )`
67///
68/// Example usage:
69/// ```
70/// # use inc_complete::{ define_intermediate, define_input, storage::SingletonStorage, impl_storage, DbHandle };
71/// # struct MyStorageType { input: SingletonStorage<MyInput>, double: SingletonStorage<Double> }
72/// # impl_storage!(MyStorageType, input:MyInput,double:Double);
73/// # #[derive(Clone)]
74/// # struct Double;
75/// # define_intermediate!(1, Double -> i32, MyStorageType, |_: &Double, db: &mut DbHandle<MyStorageType>| {
76/// #     *db.get(MyInput) * 2
77/// # });
78/// ##[derive(Clone)]
79/// struct MyInput;
80///
81/// // Define `MyInput` as an input computation with id 0 and an `i32` value
82/// // which can be used with a `Db<MyStorageType>`.
83/// define_input!(0, MyInput -> i32, MyStorageType);
84/// ```
85#[macro_export]
86macro_rules! define_input {
87    ( $id:tt, $type_name:ident -> $output_type:ty, $( $storage_type:ty )|+ ) => {
88        impl $crate::OutputType for $type_name {
89            type Output = $output_type;
90        }
91
92        impl $crate::ComputationId for $type_name {
93            fn computation_id() -> u32 {
94                $id
95            }
96        }
97
98        $(
99        impl $crate::Run<$storage_type> for $type_name {
100            fn run(&self, _: &mut $crate::DbHandle<$storage_type>) -> $output_type {
101                panic!("Attempted to call `run` function on input {}, did you forget to call `update_input`?",
102                    stringify!($type_name))
103            }
104        }
105        )+
106    };
107}
108
109/// Implements `Storage` for a struct type. This enables the given struct type `S` to be used
110/// as a generic on `Db<S>` to store _all_ computations cached by the program.
111///
112/// This will also create forwarding impls for `StorageFor<ComputationType>` for each field,
113/// computation type pair used.
114///
115/// Example usage:
116/// ```
117/// use inc_complete::{ impl_storage, define_input, define_intermediate };
118/// use inc_complete::storage::{ SingletonStorage, HashMapStorage };
119///
120/// ##[derive(Default)]
121/// struct MyStorage {
122///     foos: SingletonStorage<Foo>,
123///     bars: HashMapStorage<Bar>,
124/// }
125///
126/// impl_storage!(MyStorage,
127///     foos: Foo,
128///     bars: Bar,
129/// );
130///
131/// // Each input & intermediate computation should implement Clone
132/// ##[derive(Clone)]
133/// struct Foo;
134/// define_input!(0, Foo -> usize, MyStorage);
135///
136/// // HashMapStorage requires Eq and Hash
137/// ##[derive(Clone, PartialEq, Eq, Hash)]
138/// struct Bar(std::rc::Rc<String>);
139/// define_intermediate!(1, Bar -> usize, MyStorage, |bar, db| {
140///     bar.0.len() + *db.get(Foo)
141/// });
142/// ```
143///
144/// Note that using this macro requires each computation type to implement `Clone`.
145#[macro_export]
146macro_rules! impl_storage {
147    ($typ:ty, $( $field:ident : $computation_type:ty ),* $(, )? ) => {
148        impl $crate::Storage for $typ {
149            fn output_is_unset(&self, cell: $crate::Cell, computation_id: u32) -> bool {
150                use $crate::StorageFor;
151                match computation_id {
152                    $(
153                        x if x == <$computation_type as $crate::ComputationId>::computation_id() => {
154                            self.$field.get_output(cell).is_none()
155                        },
156                    )*
157                    id => panic!("Unknown computation id: {id}"),
158                }
159            }
160
161            fn run_computation(db: &mut $crate::DbHandle<Self>, cell: $crate::Cell, computation_id: u32) -> bool {
162                use $crate::{ StorageFor, Run };
163                match computation_id {
164                    $(
165                        x if x == <$computation_type as $crate::ComputationId>::computation_id() => {
166                            let new_value = db.storage().$field.get_input(cell).clone().run(db);
167                            db.storage_mut().$field.update_output(cell, new_value)
168                        }
169                    )*
170                    id => panic!("Unknown computation id: {id}"),
171                }
172            }
173        }
174
175        $(
176        impl $crate::StorageFor<$computation_type> for $typ {
177            fn get_cell_for_computation(&self, key: &$computation_type) -> Option<$crate::Cell> {
178                self.$field.get_cell_for_computation(key)
179            }
180
181            fn insert_new_cell(&mut self, cell: $crate::Cell, key: $computation_type) {
182                self.$field.insert_new_cell(cell, key)
183            }
184
185            fn get_input(&self, cell: $crate::Cell) -> &$computation_type {
186                self.$field.get_input(cell)
187            }
188
189            fn get_output(&self, cell: $crate::Cell) -> Option<&<$computation_type as $crate::OutputType>::Output> {
190                self.$field.get_output(cell)
191            }
192
193            fn update_output(&mut self, cell: $crate::Cell, new_value: <$computation_type as $crate::OutputType>::Output) -> bool {
194                self.$field.update_output(cell, new_value)
195            }
196        })*
197    };
198}