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_f: Example2
41///     }
42///
43///     struct Example2 {
44///         e: u8
45///     }
46///
47///     epee_object!(
48///         Example2,
49///         e: u8,
50///     );
51///
52///     epee_object!(
53///         Example,
54///         // `("ALT-NAME")` changes the name of the field in the encoded data.
55///         a("A"): u8,
56///         // `= VALUE` sets a default value that this field will be set to if not in the data
57///         // when encoding this field will be skipped if equal to the default.
58///         b: u8 = 0,
59///         // `as ALT-TYPE` encodes the data using the alt type, the alt type must impl Into<Type> and From<&Type>
60///         c: u8 as u8,
61///         // `=> read_fn, write_fn, should_write_fn,` allows you to specify alt field encoding functions.
62///         //  for the required args see the default functions, which are used here:
63///         d: u8 => cuprate_epee_encoding::read_epee_value, cuprate_epee_encoding::write_field, <u8 as cuprate_epee_encoding::EpeeValue>::should_write,
64///         // `!flatten` can be used on fields which are epee objects, and it flattens the fields of that object into this object.
65///         // So for this example `e_f` will not appear in the data but e will.
66///         // You can't use the other options with this.
67///         !flatten: e_f: Example2,
68///     );
69/// }
70/// ```
71///
72///
73#[macro_export]
74macro_rules! epee_object {
75    // ------------------------------------------------------------------------ internal_try_right_then_left
76    // All this does is return the second (right) arg if present otherwise the left is returned.
77    (
78        @internal_try_right_then_left
79        $a:expr_2021, $b:expr_2021
80    ) => {
81        $b
82    };
83
84    (
85        @internal_try_right_then_left
86        $a:expr_2021,
87    ) => {
88        $a
89    };
90
91    // ------------------------------------------------------------------------ internal_field_name
92    // Returns the alt_name if present otherwise stringifies the field ident.
93    (
94        @internal_field_name
95        $field: tt, $alt_name: tt
96    ) => {
97        $alt_name
98    };
99
100    (
101        @internal_field_name
102        $field: ident,
103    ) => {
104        stringify!($field)
105    };
106
107    // ------------------------------------------------------------------------ internal_field_type
108    // All this does is return the second (right) arg if present otherwise the left is returned.
109    (
110        @internal_field_type
111        $ty:ty, $ty_as:ty
112    ) => {
113        $ty_as
114    };
115    (
116        @internal_field_type
117        $ty:ty,
118    ) => {
119        $ty
120    };
121
122    // ------------------------------------------------------------------------ Entry Point
123    (
124        $obj:ident,
125        $($field: ident $(($alt_name: literal))?: $ty:ty $(as $ty_as:ty )? $(= $default:expr_2021)?  $(=> $read_fn:expr_2021, $write_fn:expr_2021, $should_write_fn:expr_2021)?, )*
126        $(!flatten: $flat_field: ident: $flat_ty:ty ,)*
127
128    ) => {
129        cuprate_epee_encoding::macros::paste!(
130            #[allow(non_snake_case)]
131            mod [<__epee_builder_ $obj>] {
132                use super::*;
133
134                #[derive(Default)]
135                pub struct [<__Builder $obj>] {
136                    $($field: Option<cuprate_epee_encoding::epee_object!(@internal_field_type $ty, $($ty_as)?)>,)*
137                    $($flat_field: <$flat_ty as cuprate_epee_encoding::EpeeObject>::Builder,)*
138                }
139
140                impl cuprate_epee_encoding::EpeeObjectBuilder<$obj> for [<__Builder $obj>] {
141                    fn add_field<B: cuprate_epee_encoding::macros::bytes::Buf>(&mut self, name: &str, b: &mut B) -> cuprate_epee_encoding::error::Result<bool> {
142                        match name {
143                            $(cuprate_epee_encoding::epee_object!(@internal_field_name $field, $($alt_name)?) => {
144                                if core::mem::replace(&mut self.$field, Some(
145                                    cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::read_epee_value(b)?, $($read_fn(b)?)?)
146                                )).is_some() {
147                                    Err(cuprate_epee_encoding::error::Error::Value(format!("Duplicate field in data: {}", cuprate_epee_encoding::epee_object!(@internal_field_name$field, $($alt_name)?))))?;
148                                }
149                                Ok(true)
150                            },)*
151                            _ => {
152
153                                $(if self.$flat_field.add_field(name, b)? {
154                                    return Ok(true);
155                                })*
156
157                                Ok(false)
158                            }
159                        }
160                    }
161
162                    fn finish(self) -> cuprate_epee_encoding::error::Result<$obj> {
163                        Ok(
164                            $obj {
165                                $(
166                                  $field: {
167                                      let epee_default_value = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::epee_default_value(), $({
168                                            let _ = $should_write_fn;
169                                            None
170                                      })?);
171
172                                      self.$field
173                                            $(.or(Some($default)))?
174                                            .or(epee_default_value)
175                                            $(.map(<$ty_as>::into))?
176                                              .ok_or(cuprate_epee_encoding::error::Error::Value(format!("Missing field in data: {}", cuprate_epee_encoding::epee_object!(@internal_field_name$field, $($alt_name)?))))?
177                                  },
178                                )*
179
180                                $(
181                                    $flat_field: self.$flat_field.finish()?,
182                                )*
183
184                            }
185                        )
186                    }
187                }
188            }
189
190            impl cuprate_epee_encoding::EpeeObject for $obj {
191                type Builder = [<__epee_builder_ $obj>]::[<__Builder $obj>];
192
193                fn number_of_fields(&self) -> u64 {
194                    let mut fields = 0;
195
196                    $(
197                    let field = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left &self.$field, $(<&$ty_as>::from(&self.$field))? );
198
199                      if $((field) != &$default &&)? cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(field )
200                      {
201                          fields += 1;
202                      }
203                    )*
204
205                    $(
206                        fields += self.$flat_field.number_of_fields();
207                    )*
208
209                    fields
210                }
211
212                fn write_fields<B: cuprate_epee_encoding::macros::bytes::BufMut>(self, w: &mut B) -> cuprate_epee_encoding::error::Result<()> {
213                    $(
214                    let field = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left self.$field, $(<$ty_as>::from(self.$field))? );
215
216                      if $(field != $default &&)? cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(&field )
217                        {
218                         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)?;
219                      }
220                    )*
221
222                    $(
223                        self.$flat_field.write_fields(w)?;
224                    )*
225
226                    Ok(())
227                }
228            }
229        );
230    };
231}