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}