1#[macro_export]
3macro_rules! setup_input_struct {
4 (
5 attrs: [$(#[$attr:meta]),*],
7
8 vis: $vis:vis,
10
11 Struct: $Struct:ident,
13
14 new_fn: $new_fn:ident,
16
17 field_options: [$($field_option:tt),*],
19
20 field_ids: [$($field_id:ident),*],
22
23 field_getters: [$($field_getter_vis:vis $field_getter_id:ident),*],
25
26 field_setters: [$($field_setter_vis:vis $field_setter_id:ident),*],
28
29 field_tys: [$($field_ty:ty),*],
31
32 field_indices: [$($field_index:tt),*],
34
35 required_fields: [$($required_field_id:ident $required_field_ty:ty),*],
37
38 field_durability_ids: [$($field_durability_id:ident),*],
40
41 num_fields: $N:literal,
43
44 is_singleton: $is_singleton:tt,
46
47 generate_debug_impl: $generate_debug_impl:tt,
49
50 unused_names: [
54 $zalsa:ident,
55 $zalsa_struct:ident,
56 $Configuration:ident,
57 $Builder:ident,
58 $CACHE:ident,
59 $Db:ident,
60 ]
61 ) => {
62 $(#[$attr])*
63 #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
64 $vis struct $Struct(salsa::Id);
65
66 const _: () = {
67 use salsa::plumbing as $zalsa;
68 use $zalsa::input as $zalsa_struct;
69
70 type $Configuration = $Struct;
71
72 impl $zalsa_struct::Configuration for $Configuration {
73 const DEBUG_NAME: &'static str = stringify!($Struct);
74 const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*];
75 type Singleton = $zalsa::macro_if! {if $is_singleton {$zalsa::input::Singleton} else {$zalsa::input::NotSingleton}};
76
77 type Struct = $Struct;
79
80 type Fields = ($($field_ty,)*);
82
83 type Stamps = $zalsa::Array<$zalsa::Stamp, $N>;
85 }
86
87 impl $Configuration {
88 pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<Self> {
89 static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
90 $zalsa::IngredientCache::new();
91 CACHE.get_or_create(db, || {
92 db.zalsa().add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
93 })
94 }
95
96 pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
97 let zalsa_mut = db.zalsa_mut();
98 let index = zalsa_mut.add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default());
99 let current_revision = zalsa_mut.current_revision();
100 let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
101 let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
102 (ingredient, runtime)
103 }
104 }
105
106 impl $zalsa::FromId for $Struct {
107 fn from_id(id: salsa::Id) -> Self {
108 Self(id)
109 }
110 }
111
112 impl $zalsa::AsId for $Struct {
113 fn as_id(&self) -> salsa::Id {
114 self.0
115 }
116 }
117
118 unsafe impl $zalsa::Update for $Struct {
119 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
120 if unsafe { *old_pointer } != new_value {
121 unsafe { *old_pointer = new_value };
122 true
123 } else {
124 false
125 }
126 }
127 }
128
129 $zalsa::macro_if! { $generate_debug_impl =>
130 impl std::fmt::Debug for $Struct {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 Self::default_debug_fmt(*self, f)
133 }
134 }
135 }
136
137 impl $zalsa::SalsaStructInDb for $Struct {
138 fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
139 aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
140 }
141 }
142
143 impl $Struct {
144 #[inline]
145 pub fn $new_fn<$Db>(db: &$Db, $($required_field_id: $required_field_ty),*) -> Self
146 where
147 $Db: ?Sized + salsa::Database,
149 {
150 Self::builder($($required_field_id,)*).new(db)
151 }
152
153 pub fn builder($($required_field_id: $required_field_ty),*) -> <Self as $zalsa_struct::HasBuilder>::Builder
154 {
155 builder::new_builder($($zalsa::maybe_default!($field_option, $field_ty, $field_id,)),*)
156 }
157
158 $(
159 $field_getter_vis fn $field_getter_id<'db, $Db>(self, db: &'db $Db) -> $zalsa::maybe_cloned_ty!($field_option, 'db, $field_ty)
160 where
161 $Db: ?Sized + $zalsa::Database,
163 {
164 let fields = $Configuration::ingredient(db.as_dyn_database()).field(
165 db.as_dyn_database(),
166 self,
167 $field_index,
168 );
169 $zalsa::maybe_clone!(
170 $field_option,
171 $field_ty,
172 &fields.$field_index,
173 )
174 }
175 )*
176
177 $(
178 #[must_use]
179 $field_setter_vis fn $field_setter_id<'db, $Db>(self, db: &'db mut $Db) -> impl salsa::Setter<FieldTy = $field_ty> + 'db
180 where
181 $Db: ?Sized + $zalsa::Database,
183 {
184 let (ingredient, revision) = $Configuration::ingredient_mut(db.as_dyn_database_mut());
185 $zalsa::input::SetterImpl::new(
186 revision,
187 self,
188 $field_index,
189 ingredient,
190 |fields, f| std::mem::replace(&mut fields.$field_index, f),
191 )
192 }
193 )*
194
195 $zalsa::macro_if! { $is_singleton =>
196 pub fn try_get<$Db>(db: &$Db) -> Option<Self>
197 where
198 $Db: ?Sized + salsa::Database,
200 {
201 $Configuration::ingredient(db.as_dyn_database()).get_singleton_input()
202 }
203
204 #[track_caller]
205 pub fn get<$Db>(db: &$Db) -> Self
206 where
207 $Db: ?Sized + salsa::Database,
209 {
210 Self::try_get(db).unwrap()
211 }
212 }
213
214 pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 $zalsa::with_attached_database(|db| {
217 let fields = $Configuration::ingredient(db).leak_fields(db, this);
218 let mut f = f.debug_struct(stringify!($Struct));
219 let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
220 $(
221 let f = f.field(stringify!($field_id), &fields.$field_index);
222 )*
223 f.finish()
224 }).unwrap_or_else(|| {
225 f.debug_struct(stringify!($Struct))
226 .field("[salsa id]", &this.0)
227 .finish()
228 })
229 }
230 }
231
232 impl $zalsa_struct::HasBuilder for $Struct {
233 type Builder = builder::$Builder;
234 }
235
236 impl builder::$Builder {
239 #[must_use]
241 pub fn new<$Db>(self, db: &$Db) -> $Struct
242 where
243 $Db: ?Sized + salsa::Database
245 {
246 let current_revision = $zalsa::current_revision(db);
247 let ingredient = $Configuration::ingredient(db.as_dyn_database());
248 let (fields, stamps) = builder::builder_into_inner(self, current_revision);
249 ingredient.new_input(db.as_dyn_database(), fields, stamps)
250 }
251 }
252
253 mod builder {
254 use super::*;
255
256 use salsa::plumbing as $zalsa;
257 use $zalsa::input as $zalsa_struct;
258
259 pub(super) fn new_builder($($field_id: $field_ty),*) -> $Builder {
262 $Builder {
263 fields: ($($field_id,)*),
264 durabilities: [salsa::Durability::default(); $N],
265 }
266 }
267
268 pub(super) fn builder_into_inner(builder: $Builder, revision: $zalsa::Revision) -> (($($field_ty,)*), $zalsa::Array<$zalsa::Stamp, $N>) {
269 let stamps = $zalsa::Array::new([
270 $($zalsa::stamp(revision, builder.durabilities[$field_index])),*
271 ]);
272
273 (builder.fields, stamps)
274 }
275
276 #[must_use]
277 pub struct $Builder {
278 fields: ($($field_ty,)*),
280
281 durabilities: [salsa::Durability; $N],
283 }
284
285 impl $Builder {
286 pub fn durability(mut self, durability: salsa::Durability) -> Self {
290 self.durabilities = [durability; $N];
291 self
292 }
293
294 $($zalsa::maybe_default_tt! { $field_option =>
295 #[must_use]
297 pub fn $field_id(mut self, value: $field_ty) -> Self
298 {
299 self.fields.$field_index = value;
300 self
301 }
302 })*
303
304 $(
305 #[must_use]
307 pub fn $field_durability_id(mut self, durability: salsa::Durability) -> Self
308 {
309 self.durabilities[$field_index] = durability;
310 self
311 }
312 )*
313 }
314 }
315 };
316 };
317}