compact_thrift_runtime/
macros.rs

1#[macro_export]
2macro_rules! thrift {
3    ($(#[$($def_attrs:tt)*])* struct $identifier:ident { $($definitions:tt)* } $($remainder:tt)*) => {
4        $crate::thrift_struct!($(#[$($def_attrs)*])* struct $identifier { $($definitions)* });
5        $crate::thrift!($($remainder)*);
6    };
7    ($(#[$($def_attrs:tt)*])* union $identifier:ident { $($definitions:tt)* } $($remainder:tt)*) => {
8        $crate::thrift_union!($(#[$($def_attrs)*])* union $identifier { $($definitions)* });
9        $crate::thrift!($($remainder)*);
10    };
11    ($(#[$($def_attrs:tt)*])* enum $identifier:ident { $($definitions:tt)* } $($remainder:tt)*) => {
12        $crate::thrift_enum!($(#[$($def_attrs)*])* enum $identifier { $($definitions)* });
13        $crate::thrift!($($remainder)*);
14    };
15    ($(#[$($def_attrs:tt)*])* namespace $identifier:ident $namespace:ident $($remainder:tt)*) => {
16        $crate::thrift!($($remainder)*);
17    };
18
19    () => {};
20}
21
22#[macro_export]
23macro_rules! thrift_struct {
24    ($(#[$($def_attrs:tt)*])* struct $identifier:ident { $($(#[$($field_attrs:tt)*])* $field_id:literal : $required_or_optional:ident $field_type:ident $(< $element_type:ident >)? $field_name:ident $(= $default_value:literal)? $(;)?)* }) => {
25        $(#[cfg_attr(not(doctest), $($def_attrs)*)])*
26        #[derive(Default, Clone, Debug, PartialEq)]
27        #[allow(non_camel_case_types)]
28        #[allow(non_snake_case)]
29        pub struct $identifier {
30            $($(#[cfg_attr(not(doctest), $($field_attrs)*)])* pub $field_name: $crate::__thrift_required_or_optional!($required_or_optional $crate::__thrift_field_type!($field_type $($element_type)?))),*
31        }
32
33        impl $identifier {
34            #[allow(clippy::too_many_arguments)]
35            pub fn new($($field_name: impl Into<$crate::__thrift_required_or_optional!($required_or_optional $crate::__thrift_field_type!($field_type $($element_type)?))>),*) -> Self {
36                Self {
37                    $($field_name: $field_name.into(),)*
38                }
39            }
40        }
41
42        #[allow(non_snake_case)]
43        impl <'i> $crate::CompactThriftProtocol<'i> for $identifier {
44            const FIELD_TYPE: u8 = 12;
45
46            #[inline(never)]
47            fn fill_thrift<T: $crate::CompactThriftInput<'i>>(&mut self, input: &mut T) -> std::result::Result<(), $crate::ThriftError> {
48                let mut last_field_id = 0_i16;
49                $($crate::__thrift_required_flag!($required_or_optional $field_name);)*
50                loop {
51                    let field_type = input.read_field_header(&mut last_field_id)?;
52                    if field_type == 0 {
53                        break;
54                    }
55
56                    match last_field_id {
57                        $($field_id => {
58                            $crate::__thrift_required_set!($required_or_optional $field_name);
59                            self.$field_name.fill_thrift_field(input, field_type)?;
60                        }),*
61                        _ => {
62                            input.skip_field(field_type)?;
63                        }
64                    }
65                }
66
67                $($crate::__thrift_required_check!($required_or_optional $identifier $field_name);)*
68
69                Ok(())
70            }
71
72            fn write_thrift<T: $crate::CompactThriftOutput>(&self, output: &mut T) -> std::result::Result<(), $crate::ThriftError> {
73                #[allow(unused_variables)]
74                #[allow(unused_mut)]
75                let mut last_field_id = 0_i16;
76                $(self.$field_name.write_thrift_field(output, $field_id, &mut last_field_id)?;)*
77                output.write_byte(0)?;
78                Ok(())
79            }
80        }
81    }
82}
83
84#[macro_export]
85macro_rules! thrift_union {
86    ($(#[$($def_attrs:tt)*])* union $identifier:ident { $($(#[$($field_attrs:tt)*])* $field_id:literal : $field_type:ident $(< $element_type:ident >)? $field_name:ident $(;)?)* }) => {
87        $(#[cfg_attr(not(doctest), $($def_attrs)*)])*
88        #[derive(Clone, Debug, PartialEq)]
89        #[allow(non_camel_case_types)]
90        #[allow(non_snake_case)]
91        pub enum $identifier {
92            $($(#[cfg_attr(not(doctest), $($field_attrs)*)])* $field_name($crate::__thrift_field_type!($field_type $($element_type)?))),*
93        }
94
95        impl Default for $identifier {
96            fn default() -> Self {
97                $crate::__thrift_union_default!($($field_name;)*)
98            }
99        }
100
101        #[allow(non_snake_case)]
102        impl <'i> $crate::CompactThriftProtocol<'i> for $identifier {
103            const FIELD_TYPE: u8 = 12;
104
105            #[inline(never)]
106            fn fill_thrift<T: $crate::CompactThriftInput<'i>>(&mut self, input: &mut T) -> std::result::Result<(), $crate::ThriftError> {
107                let mut last_field_id = 0_i16;
108                let field_type = input.read_field_header(&mut last_field_id)?;
109
110                if field_type == 0 {
111                    return Err($crate::ThriftError::InvalidType);
112                }
113
114                match last_field_id {
115                    $($field_id => {
116                        *self = Self::$field_name(Default::default());
117                        #[allow(unreachable_patterns)]
118                        match self {
119                            Self::$field_name(inner) => inner.fill_thrift(input)?,
120                            _ => unsafe { std::hint::unreachable_unchecked() },
121                        }
122                    }),*
123                    _ => {
124                        return Err($crate::ThriftError::MissingField(concat!(stringify!($struct_name), "\0").into()))
125                    }
126                }
127                let stop = input.read_byte()?;
128                if stop != 0 {
129                    return Err($crate::ThriftError::MissingStop)
130                }
131
132                Ok(())
133            }
134
135            fn write_thrift<T: $crate::CompactThriftOutput>(&self, output: &mut T) -> std::result::Result<(), $crate::ThriftError> {
136                let mut last_field_id = 0_i16;
137                match self {
138                    $(Self::$field_name(inner) => inner.write_thrift_field(output, $field_id, &mut last_field_id)?),*
139                }
140                output.write_byte(0)?;
141                Ok(())
142            }
143        }
144    }
145}
146
147#[macro_export]
148macro_rules! thrift_enum {
149    ($(#[$($def_attrs:tt)*])* enum $identifier:ident { $($(#[$($field_attrs:tt)*])* $field_name:ident = $field_value:literal;)* }) => {
150        $(#[$($def_attrs)*])*
151        #[derive(Default, Debug, Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
152        #[allow(non_camel_case_types)]
153        pub struct $identifier(pub i32);
154
155        impl From<i32> for $identifier {
156            #[inline]
157            fn from(value: i32) -> Self {
158                Self(value)
159            }
160        }
161
162        impl $identifier {
163            $(pub const $field_name: Self = Self($field_value);)*
164        }
165
166        impl <'i> $crate::CompactThriftProtocol<'i> for $identifier {
167            const FIELD_TYPE: u8 = 5; // i32
168
169            #[inline]
170            fn fill_thrift<T: $crate::CompactThriftInput<'i>>(&mut self, input: &mut T) -> std::result::Result<(), $crate::ThriftError> {
171                self.0 = input.read_i32()?;
172                Ok(())
173            }
174
175            #[inline]
176            fn write_thrift<T: $crate::CompactThriftOutput>(&self, output: &mut T) -> std::result::Result<(), $crate::ThriftError> {
177                output.write_i32(self.0)
178            }
179        }
180    }
181}
182
183#[doc(hidden)]
184#[macro_export]
185macro_rules! __thrift_union_default {
186    ($head:ident; $($tail:ident;)*) => {
187        Self::$head(Default::default())
188    };
189    () => {
190        Self
191    };
192}
193
194#[doc(hidden)]
195#[macro_export]
196macro_rules! __thrift_field_type {
197    (list $element_type:ident) => { Vec< $crate::__thrift_field_type!($element_type) > };
198    (set $element_type:ident) => { Vec< $crate::__thrift_field_type!($element_type) > };
199    (binary) => { Vec<u8> };
200    (string) => { String };
201    (byte) => { u8 };
202    (double) => { f64 };
203    ($field_type:ty) => { $field_type }; // this covers bool | i8 | i16 | i32 | i64
204    (Box $element_type:ident) => { std::boxed::Box< $crate::field_type!($element_type) > };
205    (Rc $element_type:ident) => { std::rc::Rc< $crate::__thrift_field_type!($element_type) > };
206    (Arc $element_type:ident) => { std::sync::Arc< $crate::__thrift_field_type!($element_type) > };
207}
208
209#[doc(hidden)]
210#[macro_export]
211macro_rules! __thrift_required_or_optional {
212    (required $field_type:ty) => { $field_type };
213    (optional $field_type:ty) => { Option<$field_type> };
214}
215
216#[doc(hidden)]
217#[macro_export]
218macro_rules! __thrift_required_flag {
219    (required $field_name:ident) => { let mut $field_name = false; };
220    (optional $field_name:ident) => {};
221}
222
223#[doc(hidden)]
224#[macro_export]
225macro_rules! __thrift_required_set {
226    (required $field_name:ident) => { $field_name = true; };
227    (optional $field_name:ident) => {};
228}
229
230#[doc(hidden)]
231#[macro_export]
232macro_rules! __thrift_required_check {
233    (required $struct_name:ident $field_name:ident) => {
234        if !$field_name {
235            return Err($crate::ThriftError::MissingField(concat!(stringify!($struct_name), "::", stringify!($field_name), "\0").into()))
236        }
237    };
238    (optional $struct_name:ident $field_name:ident) => {};
239}
240
241#[cfg(test)]
242#[allow(dead_code)]
243mod tests {
244    thrift! {
245        /** doc */
246        struct SomeStructure {
247            /** doc */
248            1: required i64 offset;
249            2: optional i64 length;
250            3: optional list<i64> foobar;
251            4: optional string data;
252            5: optional bool flag;
253            6: optional double value;
254        }
255        struct AnotherStructure {
256            1: required i64 foobar;
257        }
258
259        struct MilliSeconds {}
260        struct MicroSeconds {}
261        struct NanoSeconds {}
262        union TimeUnit {
263          1: MilliSeconds MILLIS
264          2: MicroSeconds MICROS
265          3: NanoSeconds NANOS
266        }
267        enum Type {
268          BOOLEAN = 0;
269          INT32 = 1;
270          INT64 = 2;
271          INT96 = 3;  // deprecated, only used by legacy implementations.
272          FLOAT = 4;
273          DOUBLE = 5;
274          BYTE_ARRAY = 6;
275          FIXED_LEN_BYTE_ARRAY = 7;
276        }
277        enum CompressionCodec {
278          UNCOMPRESSED = 0;
279          SNAPPY = 1;
280          GZIP = 2;
281          LZO = 3;
282          BROTLI = 4;  // Added in 2.4
283          LZ4 = 5;     // DEPRECATED (Added in 2.4)
284          ZSTD = 6;    // Added in 2.4
285          LZ4_RAW = 7; // Added in 2.9
286        }
287    }
288
289    thrift! {
290        struct ReferenceCounted {
291            1: required Rc<String> rc_string;
292            2: required Arc<String> arc_string;
293            3: required Rc<str> rc_str;
294            4: required Arc<str> arc_str;
295        }
296    }
297
298    #[test]
299    pub fn test_constructor() {
300        let _s = SomeStructure::new(1_i64, 2_i64, Some(vec![3_i64]), Some("foo".into()), true, 1.0);
301        let _r = ReferenceCounted::default();
302    }
303}