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, clippy::empty_structs_with_brackets)]
131 mod [<__epee_builder_ $obj>] {
132 use super::*;
133
134 #[derive(Default)]
135 #[allow(clippy::empty_structs_with_brackets)]
136 pub struct [<__Builder $obj>] {
137 $($field: Option<cuprate_epee_encoding::epee_object!(@internal_field_type $ty, $($ty_as)?)>,)*
138 $($flat_field: <$flat_ty as cuprate_epee_encoding::EpeeObject>::Builder,)*
139 }
140
141 impl cuprate_epee_encoding::EpeeObjectBuilder<$obj> for [<__Builder $obj>] {
142 fn add_field<B: cuprate_epee_encoding::macros::bytes::Buf>(&mut self, name: &str, b: &mut B) -> cuprate_epee_encoding::error::Result<bool> {
143 match name {
144 $(cuprate_epee_encoding::epee_object!(@internal_field_name $field, $($alt_name)?) => {
145 if self.$field.replace(
146 cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::read_epee_value(b)?, $($read_fn(b)?)?)
147 ).is_some() {
148 Err(cuprate_epee_encoding::error::Error::Value(format!("Duplicate field in data: {}", cuprate_epee_encoding::epee_object!(@internal_field_name$field, $($alt_name)?))))?;
149 }
150 Ok(true)
151 },)*
152 _ => {
153
154 $(if self.$flat_field.add_field(name, b)? {
155 return Ok(true);
156 })*
157
158 Ok(false)
159 }
160 }
161 }
162
163 fn finish(self) -> cuprate_epee_encoding::error::Result<$obj> {
164 Ok(
165 $obj {
166 $(
167 $field: {
168 let epee_default_value = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::epee_default_value(), $({
169 let _ = $should_write_fn;
170 None
171 })?);
172
173 self.$field
174 $(.or(Some($default)))?
175 .or(epee_default_value)
176 $(.map(<$ty_as>::into))?
177 .ok_or(cuprate_epee_encoding::error::Error::Value(format!("Missing field in data: {}", cuprate_epee_encoding::epee_object!(@internal_field_name$field, $($alt_name)?))))?
178 },
179 )*
180
181 $(
182 $flat_field: self.$flat_field.finish()?,
183 )*
184
185 }
186 )
187 }
188 }
189 }
190
191 impl cuprate_epee_encoding::EpeeObject for $obj {
192 type Builder = [<__epee_builder_ $obj>]::[<__Builder $obj>];
193
194 fn number_of_fields(&self) -> u64 {
195 let mut fields = 0;
196
197 $(
198 let field = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left &self.$field, $(<&$ty_as>::from(&self.$field))? );
199
200 if $((field) != &$default &&)? cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(field )
201 {
202 fields += 1;
203 }
204 )*
205
206 $(
207 fields += self.$flat_field.number_of_fields();
208 )*
209
210 fields
211 }
212
213 fn write_fields<B: cuprate_epee_encoding::macros::bytes::BufMut>(self, w: &mut B) -> cuprate_epee_encoding::error::Result<()> {
214 $(
215 let field = cuprate_epee_encoding::epee_object!(@internal_try_right_then_left self.$field, $(<$ty_as>::from(self.$field))? );
216
217 if $(field != $default &&)? cuprate_epee_encoding::epee_object!(@internal_try_right_then_left cuprate_epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(&field )
218 {
219 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)?;
220 }
221 )*
222
223 $(
224 self.$flat_field.write_fields(w)?;
225 )*
226
227 Ok(())
228 }
229 }
230 );
231 };
232}