holochain_integrity_types/action/
builder.rs

1use super::EntryType;
2use super::MigrationTarget;
3use super::Timestamp;
4use crate::action;
5use crate::link::LinkTag;
6use crate::link::LinkType;
7use crate::ActionUnweighed;
8use crate::ActionWeighed;
9use crate::EntryRateWeight;
10use crate::MembraneProof;
11use crate::RateWeight;
12use crate::ZomeIndex;
13use action::Dna;
14use holo_hash::ActionHash;
15use holo_hash::AgentPubKey;
16use holo_hash::AnyLinkableHash;
17use holo_hash::EntryHash;
18
19#[derive(Clone, Debug)]
20pub struct ActionBuilderCommon {
21    pub author: AgentPubKey,
22    pub timestamp: Timestamp,
23    pub action_seq: u32,
24    pub prev_action: ActionHash,
25}
26
27impl ActionBuilderCommon {
28    pub fn new(
29        author: AgentPubKey,
30        timestamp: Timestamp,
31        action_seq: u32,
32        prev_action: ActionHash,
33    ) -> Self {
34        Self {
35            author,
36            timestamp,
37            action_seq,
38            prev_action,
39        }
40    }
41}
42
43/// Builder for non-genesis Actions
44///
45/// SourceChain::put takes one of these rather than a raw Action, so that it
46/// can inject the proper values via `ActionBuilderCommon`, rather than requiring
47/// surrounding code to construct a proper Action outside of the context of
48/// the SourceChain.
49///
50/// Genesis actions cannot be built with this method, because the way
51/// SourceChain is written, the agent key is not known until after genesis is
52/// performed, and the agent key is one of the values injected rather than
53/// provided by this builder. (There is also the problem that the Dna action
54/// is a special case as it has no `prev_action`, and generalizing
55/// `ActionBuilderCommon` just to make that optional is a bit inconvenient.)
56/// `SourceChain::genesis` already handles genesis in one fell swoop.
57pub trait ActionBuilder<U: ActionUnweighed>: Sized {
58    fn build(self, common: ActionBuilderCommon) -> U;
59}
60
61macro_rules! builder_variant {
62    ( $name: ident <$weight : ty> { $($field: ident : $t: ty),* $(,)? } ) => {
63
64        #[derive(Clone, Debug, PartialEq, Eq)]
65        pub struct $name {
66            $(pub $field : $t,)*
67        }
68
69
70        #[allow(clippy::new_without_default)]
71        impl $name {
72            pub fn new($($field : $t),* ) -> Self {
73                Self {
74                    $($field,)*
75                }
76            }
77        }
78
79        impl ActionBuilder<action::$name<()>> for $name {
80            fn build(self, common: ActionBuilderCommon) -> action::$name<()> {
81                let ActionBuilderCommon {
82                    author,
83                    timestamp,
84                    action_seq,
85                    prev_action,
86                } = common;
87
88                action::$name {
89                    weight: (),
90                    author,
91                    timestamp,
92                    action_seq,
93                    prev_action,
94                    $($field : self.$field,)*
95                }
96            }
97        }
98
99
100        impl ActionWeighed for action::$name {
101            type Unweighed = action::$name<()>;
102            type Weight = $weight;
103
104            fn into_action(self) -> action::Action {
105                action::Action::$name(self)
106            }
107
108            fn unweighed(self) -> action::$name<()> {
109                action::$name::<()> {
110                    weight: (),
111                    author: self.author,
112                    timestamp: self.timestamp,
113                    action_seq: self.action_seq,
114                    prev_action: self.prev_action,
115                    $($field: self.$field),*
116                }
117            }
118        }
119
120        impl ActionUnweighed for action::$name<()> {
121            type Weighed = action::$name;
122            type Weight = $weight;
123
124            fn weighed(self, weight: $weight) -> action::$name {
125                action::$name {
126                    weight,
127                    author: self.author,
128                    timestamp: self.timestamp,
129                    action_seq: self.action_seq,
130                    prev_action: self.prev_action,
131                    $($field: self.$field),*
132                }
133            }
134        }
135
136        #[cfg(feature = "test_utils")]
137        impl action::$name {
138            pub fn from_builder(common: ActionBuilderCommon, $($field : $t),*) -> Self {
139                let builder = $name {
140                    $($field,)*
141                };
142
143                builder.build(common).weighed(Default::default())
144            }
145        }
146    };
147
148    ( $name: ident { $($field: ident : $t: ty),* $( $(,)? | $($dfield: ident : $dt: ty),* )? $(,)? } ) => {
149
150        #[derive(Clone, Debug, PartialEq, Eq)]
151        pub struct $name {
152            $(pub $field : $t,)*
153            $( $(pub $dfield : $dt),* )?
154        }
155
156        #[allow(clippy::new_without_default)]
157        impl $name {
158            pub fn new($($field : $t),* ) -> Self {
159                Self {
160                    $($field,)*
161                    $( $($dfield : Default::default()),* )?
162                }
163            }
164
165            pub fn new_full($($field : $t,)* $( $($dfield : $dt),* )? ) -> Self {
166                Self {
167                    $($field,)*
168                    $( $($dfield),* )?
169                }
170            }
171        }
172
173        impl ActionWeighed for action::$name {
174            type Unweighed = action::$name;
175            type Weight = ();
176
177            fn into_action(self) -> action::Action {
178                action::Action::$name(self)
179            }
180
181            fn unweighed(self) -> Self::Unweighed {
182                self
183            }
184
185        }
186
187        impl ActionUnweighed for action::$name {
188            type Weighed = action::$name;
189            type Weight = ();
190
191            fn weighed(self, _weight: ()) -> action::$name {
192                self
193            }
194        }
195
196        impl ActionBuilder<action::$name> for $name {
197            fn build(self, common: ActionBuilderCommon) -> action::$name {
198                let ActionBuilderCommon {
199                    author,
200                    timestamp,
201                    action_seq,
202                    prev_action,
203                } = common;
204
205                action::$name {
206                    author,
207                    timestamp,
208                    action_seq,
209                    prev_action,
210                    $($field : self.$field,)*
211                    $( $($dfield : self.$dfield),* )?
212                }
213            }
214        }
215
216        impl From<($name, ActionBuilderCommon)> for action::$name {
217            fn from((n, h): ($name, ActionBuilderCommon)) -> action::$name {
218                n.build(h)
219            }
220        }
221
222        #[cfg(feature = "test_utils")]
223        impl action::$name {
224            pub fn from_builder(common: ActionBuilderCommon, $($field : $t),*) -> Self {
225                let builder = $name {
226                    $($field,)*
227                    $( $($dfield : Default::default()),* )?
228                };
229
230                builder.build(common)
231            }
232        }
233    }
234}
235
236builder_variant!(InitZomesComplete {});
237
238builder_variant!(CreateLink<RateWeight> {
239    base_address: AnyLinkableHash,
240    target_address: AnyLinkableHash,
241    zome_index: ZomeIndex,
242    link_type: LinkType,
243    tag: LinkTag,
244});
245
246builder_variant!(DeleteLink {
247    link_add_address: ActionHash,
248    base_address: AnyLinkableHash,
249});
250
251builder_variant!(OpenChain {
252    prev_target: MigrationTarget,
253    close_hash: ActionHash,
254});
255
256builder_variant!(CloseChain {
257    new_target: Option<MigrationTarget>,
258});
259
260builder_variant!(Create<EntryRateWeight> {
261    entry_type: EntryType,
262    entry_hash: EntryHash,
263});
264
265builder_variant!(Update<EntryRateWeight> {
266    original_entry_address: EntryHash,
267    original_action_address: ActionHash,
268
269    entry_type: EntryType,
270    entry_hash: EntryHash,
271});
272
273builder_variant!(Delete<RateWeight> {
274    deletes_address: ActionHash,
275    deletes_entry_address: EntryHash,
276});
277
278builder_variant!(AgentValidationPkg {
279    membrane_proof: Option<MembraneProof>,
280});
281
282/// The Dna action can't implement ActionBuilder because it lacks a
283/// `prev_action` field, so this helper is provided as a special case
284#[cfg(feature = "test_utils")]
285impl Dna {
286    pub fn from_builder(hash: holo_hash::DnaHash, builder: ActionBuilderCommon) -> Self {
287        Self {
288            author: builder.author,
289            timestamp: builder.timestamp,
290            hash,
291        }
292    }
293}
294
295// some more manual implementations for Dna
296
297impl ActionWeighed for Dna {
298    type Unweighed = Dna;
299    type Weight = ();
300
301    fn into_action(self) -> action::Action {
302        action::Action::Dna(self)
303    }
304
305    fn unweighed(self) -> Self::Unweighed {
306        self
307    }
308}
309
310impl ActionUnweighed for Dna {
311    type Weighed = Dna;
312    type Weight = ();
313
314    fn weighed(self, _weight: ()) -> Dna {
315        self
316    }
317}