Skip to main content

cuprate_epee_encoding/
macros.rs

1pub use bytes;
2pub use paste::paste;
3
4/// Macro to derive [`EpeeObject`](crate::EpeeObject) for structs.
5///
6/// ### Basic Usage:
7///
8/// ```rust
9/// // mod visibility is here because of Rust visibility weirdness, you shouldn't need this unless defined in a function.
10/// // see: <https://github.com/rust-lang/rust/issues/64079>
11/// mod visibility {
12///
13///     use cuprate_epee_encoding::epee_object;
14///
15///     struct Example {
16///         a: u8
17///     }
18///
19///     epee_object!(
20///         Example,
21///         a: u8,
22///     );
23/// }
24/// ```
25///
26/// ### Advanced Usage:
27///
28/// ```rust
29/// // mod visibility is here because of Rust visibility weirdness, you shouldn't need this unless defined in a function.
30/// // see: <https://github.com/rust-lang/rust/issues/64079>
31/// mod visibility {
32///
33///     use cuprate_epee_encoding::epee_object;
34///
35///     struct Example {
36///         a: u8,
37///         b: u8,
38///         c: u8,
39///         d: u8,
40///         e: Vec<String>,
41///         f_f: Example2
42///     }
43///
44///     struct Example2 {
45///         f: u8
46///     }
47///
48///     epee_object!(
49///         Example2,
50///         f: u8,
51///     );
52///
53///     epee_object!(
54///         Example,
55///         // `("ALT-NAME")` changes the name of the field in the encoded data.
56///         a("A"): u8,
57///         // `= VALUE` sets a default value that this field will be set to if not in the data
58///         // when encoding this field will be skipped if equal to the default.
59///         b: u8 = 0,
60///         // `as ALT-TYPE` encodes the data using the alt type, the alt type must impl Into<Type> and From<&Type>
61///         c: u8 as u8,
62///         // `=> read_fn, write_fn, should_write_fn,` allows you to specify alt field encoding functions.
63///         //  for the required args see the default functions, which are used here:
64///         d: u8 => cuprate_epee_encoding::read_epee_value, cuprate_epee_encoding::write_field, <u8 as cuprate_epee_encoding::EpeeValue>::should_write,
65///         // `[]` allows you to set limits on fields.
66///         // `[min_element: NUMBER]` is used to set a minimum number of bytes that each element must consume.
67///         // `[max_len: NUMBER]` is used to set a maximum number of elements in a sequence.
68///         e: Vec<String> [min_element: 16, max_len: 128],
69///         // `!flatten` can be used on fields which are epee objects, and it flattens the fields of that object into this object.
70///         // So for this example `f_f` will not appear in the data but f will.
71///         // You can't use the other options with this.
72///         !flatten: f_f: Example2,
73///     );
74/// }
75/// ```
76///
77///
78#[macro_export]
79macro_rules! epee_object {
80    // ------------------------------------------------------------------------ internal_try_right_then_left
81    // All this does is return the second (right) arg if present otherwise the left is returned.
82    (
83        @internal_try_right_then_left
84        $a:expr_2021, $b:expr_2021
85    ) => {
86        $b
87    };
88
89    (
90        @internal_try_right_then_left
91        $a:expr_2021,
92    ) => {
93        $a
94    };
95
96    // ------------------------------------------------------------------------ internal_field_name
97    // Returns the alt_name if present otherwise stringifies the field ident.
98    (
99        @internal_field_name
100        $field: tt, $alt_name: tt
101    ) => {
102        $alt_name
103    };
104
105    (
106        @internal_field_name
107        $field: ident,
108    ) => {
109        stringify!($field)
110    };
111
112    // ------------------------------------------------------------------------ internal_field_type
113    // All this does is return the second (right) arg if present otherwise the left is returned.
114    (
115        @internal_field_type
116        $ty:ty, $ty_as:ty
117    ) => {
118        $ty_as
119    };
120    (
121        @internal_field_type
122        $ty:ty,
123    ) => {
124        $ty
125    };
126
127    // ------------------------------------------------------------------------ internal_sequence_limits
128    (
129        @internal_sequence_limits
130    ) => {
131        cuprate_epee_encoding::EpeeValueLimits {
132            min_element_size: 0,
133            max_sequence_len: usize::MAX,
134        }
135    };
136
137    (
138        @internal_sequence_limits
139        min_element: $min_element_size:expr_2021
140    ) => {
141        cuprate_epee_encoding::EpeeValueLimits {
142            min_element_size: $min_element_size,
143            max_sequence_len: usize::MAX,
144        }
145    };
146
147    (
148        @internal_sequence_limits
149        max_len: $max_sequence_len:expr_2021
150    ) => {
151        cuprate_epee_encoding::EpeeValueLimits {
152            min_element_size: 0,
153            max_sequence_len: $max_sequence_len,
154        }
155    };
156
157    (
158        @internal_sequence_limits
159        min_element: $min_element_size:expr_2021, max_len: $max_sequence_len:expr_2021
160    ) => {
161        cuprate_epee_encoding::EpeeValueLimits {
162            min_element_size: $min_element_size,
163            max_sequence_len: $max_sequence_len,
164        }
165    };
166
167    // ------------------------------------------------------------------------ Entry Point
168    (
169        $obj:ident,
170        $($field: ident $(($alt_name: literal))?: $ty:ty $(as $ty_as:ty )? $([$($sequence_constraints:tt)+])? $(= $default:expr_2021)?  $(=> $read_fn:expr_2021, $write_fn:expr_2021, $should_write_fn:expr_2021)?, )*
171        $(!flatten: $flat_field: ident: $flat_ty:ty ,)*
172
173    ) => {
174        cuprate_epee_encoding::macros::paste!(
175            #[allow(non_snake_case, clippy::empty_structs_with_brackets)]
176            mod [<__epee_builder_ $obj>] {
177                use super::*;
178
179                #[derive(Default)]
180                #[allow(clippy::empty_structs_with_brackets)]
181                pub struct [<__Builder $obj>] {
182                    $($field: Option<cuprate_epee_encoding::epee_object!(@internal_field_type $ty, $($ty_as)?)>,)*
183                    $($flat_field: <$flat_ty as cuprate_epee_encoding::EpeeObject>::Builder,)*
184                }
185
186                impl cuprate_epee_encoding::EpeeObjectBuilder<$obj> for [<__Builder $obj>] {
187                    fn add_field<B: cuprate_epee_encoding::macros::bytes::Buf>(&mut self, name: &str, b: &mut B) -> cuprate_epee_encoding::error::Result<bool> {
188                        match name {
189                            $(cuprate_epee_encoding::epee_object!(@internal_field_name $field, $($alt_name)?) => {
190                                let limits = cuprate_epee_encoding::epee_object!(
191                                    @internal_sequence_limits $($($sequence_constraints)+)?
192                                );
193                                if self.$field.replace(
194                                    cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::read_epee_value(b, limits)?, $($read_fn(b, limits)?)?)
195                                ).is_some() {
196                                    Err(cuprate_epee_encoding::error::Error::Value(format!("Duplicate field in data: {}", cuprate_epee_encoding::epee_object!(@internal_field_name$field, $($alt_name)?))))?;
197                                }
198                                Ok(true)
199                            },)*
200                            _ => {
201
202                                $(if self.$flat_field.add_field(name, b)? {
203                                    return Ok(true);
204                                })*
205
206                                Ok(false)
207                            }
208                        }
209                    }
210
211                    fn finish(self) -> cuprate_epee_encoding::error::Result<$obj> {
212                        Ok(
213                            $obj {
214                                $(
215                                  $field: {
216                                      let epee_default_value = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::epee_default_value(), $({
217                                            let _ = $should_write_fn;
218                                            None
219                                      })?);
220
221                                      self.$field
222                                            $(.or(Some($default)))?
223                                            .or(epee_default_value)
224                                            $(.map(<$ty_as>::into))?
225                                              .ok_or(cuprate_epee_encoding::error::Error::Value(format!("Missing field in data: {}", cuprate_epee_encoding::epee_object!(@internal_field_name$field, $($alt_name)?))))?
226                                  },
227                                )*
228
229                                $(
230                                    $flat_field: self.$flat_field.finish()?,
231                                )*
232
233                            }
234                        )
235                    }
236                }
237            }
238
239            impl cuprate_epee_encoding::EpeeObject for $obj {
240                type Builder = [<__epee_builder_ $obj>]::[<__Builder $obj>];
241
242                fn number_of_fields(&self) -> u64 {
243                    let mut fields = 0;
244
245                    $(
246                    let field = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left &self.$field, $(<&$ty_as>::from(&self.$field))? );
247
248                      if $((field) != &$default &&)? cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(field )
249                      {
250                          fields += 1;
251                      }
252                    )*
253
254                    $(
255                        fields += self.$flat_field.number_of_fields();
256                    )*
257
258                    fields
259                }
260
261                fn write_fields<B: cuprate_epee_encoding::macros::bytes::BufMut>(self, w: &mut B) -> cuprate_epee_encoding::error::Result<()> {
262                    $(
263                    let field = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left self.$field, $(<$ty_as>::from(self.$field))? );
264
265                      if $(field != $default &&)? cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(&field )
266                        {
267                         cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::write_field, $($write_fn)?)((field), cuprate_epee_encoding::epee_object!(@internal_field_name$field, $($alt_name)?), w)?;
268                      }
269                    )*
270
271                    $(
272                        self.$flat_field.write_fields(w)?;
273                    )*
274
275                    Ok(())
276                }
277            }
278        );
279    };
280}