Skip to main content

parquet/
parquet_macros.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18// These macros are adapted from Jörn Horstmann's thrift macros at
19// https://github.com/jhorstmann/compact-thrift
20// They allow for pasting sections of the Parquet thrift IDL file
21// into a macro to generate rust structures and implementations.
22
23//! This is a collection of macros used to parse Thrift IDL descriptions of structs,
24//! unions, and enums into their corresponding Rust types. These macros will also
25//! generate the code necessary to serialize and deserialize to/from the [Thrift compact]
26//! protocol.
27//!
28//! Further details of how to use them (and other aspects of the Thrift serialization process)
29//! can be found in [THRIFT.md].
30//!
31//! [Thrift compact]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md#list-and-set
32//! [THRIFT.md]: https://github.com/apache/arrow-rs/blob/main/parquet/THRIFT.md
33
34// These macros generate `allow` attributes for lints that may not fire on every
35// invocation, so we keep `allow` instead of `expect`.
36
37#[doc(hidden)]
38#[macro_export]
39#[expect(clippy::allow_attributes)]
40#[allow(clippy::crate_in_macro_def)]
41/// Macro used to generate rust enums from a Thrift `enum` definition.
42///
43/// Note:
44///  - All enums generated with this macro will have `pub` visibility.
45///  - When utilizing this macro the Thrift serialization traits and structs need to be in scope.
46macro_rules! thrift_enum {
47    ($(#[$($def_attrs:tt)*])* enum $identifier:ident { $($(#[$($field_attrs:tt)*])* $field_name:ident = $field_value:literal;)* }) => {
48        $(#[$($def_attrs)*])*
49        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
50        #[allow(clippy::allow_attributes)]
51        #[allow(non_camel_case_types)]
52        #[allow(missing_docs)]
53        pub enum $identifier {
54            $($(#[cfg_attr(not(doctest), $($field_attrs)*)])* $field_name = $field_value,)*
55        }
56
57        impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for $identifier {
58            #[allow(clippy::allow_attributes)]
59            #[allow(deprecated)]
60            fn read_thrift(prot: &mut R) -> Result<Self> {
61                let val = prot.read_i32()?;
62                match val {
63                    $($field_value => Ok(Self::$field_name),)*
64                    _ => Err(general_err!("Unexpected {} {}", stringify!($identifier), val)),
65                }
66            }
67        }
68
69        impl fmt::Display for $identifier {
70            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71                write!(f, "{self:?}")
72            }
73        }
74
75        impl WriteThrift for $identifier {
76            const ELEMENT_TYPE: ElementType = ElementType::I32;
77
78            fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
79                writer.write_i32(*self as i32)
80            }
81        }
82
83        impl WriteThriftField for $identifier {
84            fn write_thrift_field<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>, field_id: i16, last_field_id: i16) -> Result<i16> {
85                writer.write_field_begin(FieldType::I32, field_id, last_field_id)?;
86                self.write_thrift(writer)?;
87                Ok(field_id)
88            }
89        }
90
91        impl $identifier {
92            #[allow(clippy::allow_attributes)]
93            #[allow(deprecated)]
94            #[doc = "Returns a slice containing every variant of this enum."]
95            #[allow(dead_code)]
96            pub const VARIANTS: &'static [Self] = &[
97                $(Self::$field_name),*
98            ];
99
100            #[allow(clippy::allow_attributes)]
101            #[allow(deprecated)]
102            const fn max_discriminant_impl() -> i32 {
103                let values: &[i32] = &[$($field_value),*];
104                let mut max = values[0];
105                let mut idx = 1;
106                while idx < values.len() {
107                    let candidate = values[idx];
108                    if candidate > max {
109                        max = candidate;
110                    }
111                    idx += 1;
112                }
113                max
114            }
115
116            #[allow(clippy::allow_attributes)]
117            #[allow(deprecated)]
118            #[doc = "Returns the largest discriminant value defined for this enum."]
119            #[allow(dead_code)]
120            pub const MAX_DISCRIMINANT: i32 = Self::max_discriminant_impl();
121        }
122    }
123}
124
125/// Macro used to generate Rust enums for Thrift unions in which all variants are typed with empty
126/// structs.
127///
128/// Because the compact protocol does not write any struct type information, these empty structs
129/// become a single `0` (end-of-fields marker) upon serialization. Rather than trying to deserialize
130/// an empty struct, we can instead simply read the `0` and discard it.
131///
132/// The resulting Rust enum will have all unit variants.
133///
134/// Note:
135///  - All enums generated with this macro will have `pub` visibility.
136///  - When utilizing this macro the Thrift serialization traits and structs need to be in scope.
137#[doc(hidden)]
138#[macro_export]
139#[expect(clippy::allow_attributes)]
140#[allow(clippy::crate_in_macro_def)]
141macro_rules! thrift_union_all_empty {
142    ($(#[$($def_attrs:tt)*])* union $identifier:ident { $($(#[$($field_attrs:tt)*])* $field_id:literal : $field_type:ident $(< $element_type:ident >)? $field_name:ident $(;)?)* }) => {
143        $(#[cfg_attr(not(doctest), $($def_attrs)*)])*
144        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
145        #[allow(clippy::allow_attributes)]
146        #[allow(non_camel_case_types)]
147        #[allow(non_snake_case)]
148        #[allow(missing_docs)]
149        pub enum $identifier {
150            $($(#[cfg_attr(not(doctest), $($field_attrs)*)])* $field_name),*
151        }
152
153        impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for $identifier {
154            fn read_thrift(prot: &mut R) -> Result<Self> {
155                let field_ident = prot.read_field_begin(0)?;
156                if field_ident.field_type == FieldType::Stop {
157                    return Err(general_err!("Received empty union from remote {}", stringify!($identifier)));
158                }
159                let ret = match field_ident.id {
160                    $($field_id => {
161                        prot.skip_empty_struct()?;
162                        Self::$field_name
163                    }
164                    )*
165                    _ => {
166                        return Err(general_err!("Unexpected {} {}", stringify!($identifier), field_ident.id));
167                    }
168                };
169                let field_ident = prot.read_field_begin(field_ident.id)?;
170                if field_ident.field_type != FieldType::Stop {
171                    return Err(general_err!(
172                        "Received multiple fields for union from remote {}", stringify!($identifier)
173                    ));
174                }
175                Ok(ret)
176            }
177        }
178
179        impl WriteThrift for $identifier {
180            const ELEMENT_TYPE: ElementType = ElementType::Struct;
181
182            fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
183                match *self {
184                    $(Self::$field_name => writer.write_empty_struct($field_id, 0)?,)*
185                };
186                // write end of struct for this union
187                writer.write_struct_end()
188            }
189        }
190
191        impl WriteThriftField for $identifier {
192            fn write_thrift_field<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>, field_id: i16, last_field_id: i16) -> Result<i16> {
193                writer.write_field_begin(FieldType::Struct, field_id, last_field_id)?;
194                self.write_thrift(writer)?;
195                Ok(field_id)
196            }
197        }
198    }
199}
200
201/// Macro used to generate Rust enums for Thrift unions where variants are a mix of unit and
202/// tuple types.
203///
204/// Use of this macro requires modifying the thrift IDL. For variants with empty structs as their
205/// type, delete the typename (i.e. `1: EmptyStruct Var1;` becomes `1: Var1`). For variants with a
206/// non-empty type, the typename must be contained within parens (e.g. `1: MyType Var1;` becomes
207/// `1: (MyType) Var1;`).
208///
209/// Note:
210///  - All enums generated with this macro will have `pub` visibility.
211///  - This macro allows for specifying lifetime annotations for the resulting `enum` and its fields.
212///  - When utilizing this macro the Thrift serialization traits and structs need to be in scope.
213#[doc(hidden)]
214#[macro_export]
215#[expect(clippy::allow_attributes)]
216#[allow(clippy::crate_in_macro_def)]
217macro_rules! thrift_union {
218    ($(#[$($def_attrs:tt)*])* union $identifier:ident $(< $lt:lifetime >)? { $($(#[$($field_attrs:tt)*])* $field_id:literal : $( ( $field_type:ident $(< $element_type:ident >)? $(< $field_lt:lifetime >)?) )? $field_name:ident $(;)?)* }) => {
219        $(#[cfg_attr(not(doctest), $($def_attrs)*)])*
220        #[derive(Clone, Debug, Eq, PartialEq)]
221        #[allow(clippy::allow_attributes)]
222        #[allow(non_camel_case_types)]
223        #[allow(non_snake_case)]
224        #[allow(missing_docs)]
225        pub enum $identifier $(<$lt>)? {
226            $($(#[cfg_attr(not(doctest), $($field_attrs)*)])* $field_name $( ( $crate::__thrift_union_type!{$field_type $($field_lt)? $($element_type)?} ) )?),*
227        }
228
229        impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for $identifier $(<$lt>)? {
230            fn read_thrift(prot: &mut R) -> Result<Self> {
231                let field_ident = prot.read_field_begin(0)?;
232                if field_ident.field_type == FieldType::Stop {
233                    return Err(general_err!("Received empty union from remote {}", stringify!($identifier)));
234                }
235                let ret = match field_ident.id {
236                    $($field_id => {
237                        let val = $crate::__thrift_read_variant!(prot, $field_name $($field_type $($element_type)?)?);
238                        val
239                    })*
240                    _ => {
241                        return Err(general_err!("Unexpected {} {}", stringify!($identifier), field_ident.id));
242                    }
243                };
244                let field_ident = prot.read_field_begin(field_ident.id)?;
245                if field_ident.field_type != FieldType::Stop {
246                    return Err(general_err!(
247                        concat!("Received multiple fields for union from remote {}", stringify!($identifier))
248                    ));
249                }
250                Ok(ret)
251            }
252        }
253
254        impl $(<$lt>)? WriteThrift for $identifier $(<$lt>)? {
255            const ELEMENT_TYPE: ElementType = ElementType::Struct;
256
257            fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
258                match self {
259                    $($crate::__thrift_write_variant_lhs!($field_name $($field_type)?, variant_val) =>
260                      $crate::__thrift_write_variant_rhs!($field_id $($field_type)?, writer, variant_val),)*
261                };
262                writer.write_struct_end()
263            }
264        }
265
266        impl $(<$lt>)? WriteThriftField for $identifier $(<$lt>)? {
267            fn write_thrift_field<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>, field_id: i16, last_field_id: i16) -> Result<i16> {
268                writer.write_field_begin(FieldType::Struct, field_id, last_field_id)?;
269                self.write_thrift(writer)?;
270                Ok(field_id)
271            }
272        }
273    }
274}
275
276/// Macro used to generate Rust enums for Thrift unions where variants are a mix of unit and
277/// tuple types. This version allows for unknown variants for forwards compatibility.
278///
279/// Use of this macro requires modifying the thrift IDL. For variants with empty structs as their
280/// type, delete the typename (i.e. `1: EmptyStruct Var1;` becomes `1: Var1`). For variants with a
281/// non-empty type, the typename must be contained within parens (e.g. `1: MyType Var1;` becomes
282/// `1: (MyType) Var1;`).
283///
284/// This macro allows for specifying lifetime annotations for the resulting `enum` and its fields.
285///
286/// When utilizing this macro the Thrift serialization traits and structs need to be in scope.
287#[doc(hidden)]
288#[macro_export]
289#[expect(clippy::allow_attributes)]
290#[allow(clippy::crate_in_macro_def)]
291macro_rules! thrift_union_with_unknown {
292    ($(#[$($def_attrs:tt)*])* union $identifier:ident $(< $lt:lifetime >)? { $($(#[$($field_attrs:tt)*])* $field_id:literal : $( ( $field_type:ident $(< $element_type:ident >)? $(< $field_lt:lifetime >)?) )? $field_name:ident $(;)?)* }) => {
293        $(#[cfg_attr(not(doctest), $($def_attrs)*)])*
294        #[derive(Clone, Debug, Eq, PartialEq)]
295        #[allow(clippy::allow_attributes)]
296        #[allow(non_camel_case_types)]
297        #[allow(non_snake_case)]
298        #[allow(missing_docs)]
299        pub enum $identifier $(<$lt>)? {
300            $($(#[cfg_attr(not(doctest), $($field_attrs)*)])* $field_name $( ( $crate::__thrift_union_type!{$field_type $($field_lt)? $($element_type)?} ) )?),*,
301            _Unknown {
302                /// The field id encountered when parsing the unknown variant.
303                field_id: i16,
304            },
305        }
306
307        impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for $identifier $(<$lt>)? {
308            fn read_thrift(prot: &mut R) -> Result<Self> {
309                let field_ident = prot.read_field_begin(0)?;
310                if field_ident.field_type == FieldType::Stop {
311                    return Err(general_err!("Received empty union from remote {}", stringify!($identifier)));
312                }
313                let ret = match field_ident.id {
314                    $($field_id => {
315                        let val = $crate::__thrift_read_variant!(prot, $field_name $($field_type $($element_type)?)?);
316                        val
317                    })*
318                    _ => {
319                        prot.skip(field_ident.field_type)?;
320                        Self::_Unknown {
321                            field_id: field_ident.id,
322                        }
323                    }
324                };
325                let field_ident = prot.read_field_begin(field_ident.id)?;
326                if field_ident.field_type != FieldType::Stop {
327                    return Err(general_err!(
328                        concat!("Received multiple fields for union from remote {}", stringify!($identifier))
329                    ));
330                }
331                Ok(ret)
332            }
333        }
334
335        impl $(<$lt>)? WriteThrift for $identifier $(<$lt>)? {
336            const ELEMENT_TYPE: ElementType = ElementType::Struct;
337
338            fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
339                match self {
340                    $($crate::__thrift_write_variant_lhs!($field_name $($field_type)?, variant_val) =>
341                      $crate::__thrift_write_variant_rhs!($field_id $($field_type)?, writer, variant_val),)*
342                    Self::_Unknown{..} => return Err(general_err!("Trying to write unknown variant")),
343                };
344                writer.write_struct_end()
345            }
346        }
347
348        impl $(<$lt>)? WriteThriftField for $identifier $(<$lt>)? {
349            fn write_thrift_field<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>, field_id: i16, last_field_id: i16) -> Result<i16> {
350                writer.write_field_begin(FieldType::Struct, field_id, last_field_id)?;
351                self.write_thrift(writer)?;
352                Ok(field_id)
353            }
354        }
355    }
356}
357
358/// Macro used to generate Rust structs from a Thrift `struct` definition.
359///
360/// Note:
361///  - This macro allows for specifying the visibility of the resulting `struct` and its fields.
362///    + The `struct` and all fields will have the same visibility.
363///  - This macro allows for specifying lifetime annotations for the resulting `struct` and its fields.
364///  - When utilizing this macro the Thrift serialization traits and structs need to be in scope.
365#[doc(hidden)]
366#[macro_export]
367macro_rules! thrift_struct {
368    ($(#[$($def_attrs:tt)*])* $vis:vis struct $identifier:ident $(< $lt:lifetime >)? { $($(#[$($field_attrs:tt)*])* $field_id:literal : $required_or_optional:ident $field_type:ident $(< $field_lt:lifetime >)? $(< $element_type:ident >)? $field_name:ident $(= $default_value:literal)? $(;)?)* }) => {
369        $(#[cfg_attr(not(doctest), $($def_attrs)*)])*
370        #[derive(Clone, Debug, Eq, PartialEq)]
371        #[allow(clippy::allow_attributes)]
372        #[allow(non_camel_case_types)]
373        #[allow(non_snake_case)]
374        #[allow(missing_docs)]
375        $vis struct $identifier $(<$lt>)? {
376            $($(#[cfg_attr(not(doctest), $($field_attrs)*)])* $vis $field_name: $crate::__thrift_required_or_optional!($required_or_optional $crate::__thrift_field_type!($field_type $($field_lt)? $($element_type)?))),*
377        }
378
379        impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for $identifier $(<$lt>)? {
380            fn read_thrift(prot: &mut R) -> Result<Self> {
381                $(let mut $field_name: Option<$crate::__thrift_field_type!($field_type $($field_lt)? $($element_type)?)> = None;)*
382                let mut last_field_id = 0i16;
383                loop {
384                    let field_ident = prot.read_field_begin(last_field_id)?;
385                    if field_ident.field_type == FieldType::Stop {
386                        break;
387                    }
388                    match field_ident.id {
389                        $($field_id => {
390                            let val = $crate::__thrift_read_field!(prot, field_ident, $field_type $($field_lt)? $($element_type)?);
391                            $field_name = Some(val);
392                        })*
393                        _ => {
394                            prot.skip(field_ident.field_type)?;
395                        }
396                    };
397                    last_field_id = field_ident.id;
398                }
399                $($crate::__thrift_result_required_or_optional!($required_or_optional $field_name);)*
400                Ok(Self {
401                    $($field_name),*
402                })
403            }
404        }
405
406        impl $(<$lt>)? WriteThrift for $identifier $(<$lt>)? {
407            const ELEMENT_TYPE: ElementType = ElementType::Struct;
408
409            #[allow(clippy::allow_attributes)]
410            #[allow(unused_assignments)]
411            fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
412                #[allow(unused_mut, unused_variables)]
413                let mut last_field_id = 0i16;
414                $($crate::__thrift_write_required_or_optional_field!($required_or_optional $field_name, $field_id, $field_type, self, writer, last_field_id);)*
415                writer.write_struct_end()
416            }
417        }
418
419        impl $(<$lt>)? WriteThriftField for $identifier $(<$lt>)? {
420            fn write_thrift_field<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>, field_id: i16, last_field_id: i16) -> Result<i16> {
421                writer.write_field_begin(FieldType::Struct, field_id, last_field_id)?;
422                self.write_thrift(writer)?;
423                Ok(field_id)
424            }
425        }
426    }
427}
428
429#[doc(hidden)]
430#[macro_export]
431/// Generate `WriteThriftField` implementation for a struct.
432macro_rules! write_thrift_field {
433    ($identifier:ident $(< $lt:lifetime >)?, $fld_type:expr) => {
434        impl $(<$lt>)? WriteThriftField for $identifier $(<$lt>)? {
435            fn write_thrift_field<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>, field_id: i16, last_field_id: i16) -> Result<i16> {
436                writer.write_field_begin($fld_type, field_id, last_field_id)?;
437                self.write_thrift(writer)?;
438                Ok(field_id)
439            }
440        }
441    }
442}
443
444#[doc(hidden)]
445#[macro_export]
446macro_rules! __thrift_write_required_or_optional_field {
447    (required $field_name:ident, $field_id:literal, $field_type:ident, $self:tt, $writer:tt, $last_id:tt) => {
448        $crate::__thrift_write_required_field!(
449            $field_type,
450            $field_name,
451            $field_id,
452            $self,
453            $writer,
454            $last_id
455        )
456    };
457    (optional $field_name:ident, $field_id:literal, $field_type:ident, $self:tt, $writer:tt, $last_id:tt) => {
458        $crate::__thrift_write_optional_field!(
459            $field_type,
460            $field_name,
461            $field_id,
462            $self,
463            $writer,
464            $last_id
465        )
466    };
467}
468
469#[doc(hidden)]
470#[macro_export]
471macro_rules! __thrift_write_required_field {
472    (binary, $field_name:ident, $field_id:literal, $self:ident, $writer:ident, $last_id:ident) => {
473        $writer.write_field_begin(FieldType::Binary, $field_id, $last_id)?;
474        $writer.write_bytes($self.$field_name)?;
475        $last_id = $field_id;
476    };
477    ($field_type:ident, $field_name:ident, $field_id:literal, $self:ident, $writer:ident, $last_id:ident) => {
478        $last_id = $self
479            .$field_name
480            .write_thrift_field($writer, $field_id, $last_id)?;
481    };
482}
483
484#[doc(hidden)]
485#[macro_export]
486macro_rules! __thrift_write_optional_field {
487    (binary, $field_name:ident, $field_id:literal, $self:ident, $writer:tt, $last_id:tt) => {
488        if $self.$field_name.is_some() {
489            $writer.write_field_begin(FieldType::Binary, $field_id, $last_id)?;
490            $writer.write_bytes($self.$field_name.as_ref().unwrap())?;
491            $last_id = $field_id;
492        }
493    };
494    ($field_type:ident, $field_name:ident, $field_id:literal, $self:ident, $writer:tt, $last_id:tt) => {
495        if $self.$field_name.is_some() {
496            $last_id = $self
497                .$field_name
498                .as_ref()
499                .unwrap()
500                .write_thrift_field($writer, $field_id, $last_id)?;
501        }
502    };
503}
504
505#[doc(hidden)]
506#[macro_export]
507macro_rules! __thrift_required_or_optional {
508    (required $field_type:ty) => { $field_type };
509    (optional $field_type:ty) => { Option<$field_type> };
510}
511
512// Performance note: using `expect` here is about 4% faster on the page index bench,
513// but we want to propagate errors. Using `ok_or` is *much* slower.
514#[doc(hidden)]
515#[macro_export]
516macro_rules! __thrift_result_required_or_optional {
517    (required $field_name:ident) => {
518        let Some($field_name) = $field_name else {
519            return Err(general_err!(concat!(
520                "Required field ",
521                stringify!($field_name),
522                " is missing",
523            )));
524        };
525    };
526    (optional $field_name:ident) => {};
527}
528
529#[doc(hidden)]
530#[macro_export]
531macro_rules! __thrift_read_field {
532    ($prot:tt, $field_ident:tt, list $lt:lifetime binary) => {
533        read_thrift_vec::<&'a [u8], R>(&mut *$prot)?
534    };
535    ($prot:tt, $field_ident:tt, list $lt:lifetime $element_type:ident) => {
536        read_thrift_vec::<$element_type, R>(&mut *$prot)?
537    };
538    ($prot:tt, $field_ident:tt, list string) => {
539        read_thrift_vec::<String, R>(&mut *$prot)?
540    };
541    ($prot:tt, $field_ident:tt, list $element_type:ident) => {
542        read_thrift_vec::<$element_type, R>(&mut *$prot)?
543    };
544    ($prot:tt, $field_ident:tt, string $lt:lifetime) => {
545        <&$lt str>::read_thrift(&mut *$prot)?
546    };
547    ($prot:tt, $field_ident:tt, binary $lt:lifetime) => {
548        <&$lt [u8]>::read_thrift(&mut *$prot)?
549    };
550    ($prot:tt, $field_ident:tt, $field_type:ident $lt:lifetime) => {
551        $field_type::read_thrift(&mut *$prot)?
552    };
553    ($prot:tt, $field_ident:tt, string) => {
554        String::read_thrift(&mut *$prot)?
555    };
556    ($prot:tt, $field_ident:tt, binary) => {
557        // this one needs to not conflict with `list<i8>`
558        $prot.read_bytes_owned()?
559    };
560    ($prot:tt, $field_ident:tt, double) => {
561        $crate::parquet_thrift::OrderedF64::read_thrift(&mut *$prot)?
562    };
563    ($prot:tt, $field_ident:tt, bool) => {
564        $field_ident.bool_val()?
565    };
566    ($prot:tt, $field_ident:tt, $field_type:ident) => {
567        $field_type::read_thrift(&mut *$prot)?
568    };
569}
570
571#[doc(hidden)]
572#[macro_export]
573macro_rules! __thrift_field_type {
574    (binary $lt:lifetime) => { &$lt [u8] };
575    (string $lt:lifetime) => { &$lt str };
576    ($field_type:ident $lt:lifetime) => { $field_type<$lt> };
577    (list $lt:lifetime $element_type:ident) => { Vec< $crate::__thrift_field_type!($element_type $lt) > };
578    (list string) => { Vec<String> };
579    (list $element_type:ident) => { Vec< $crate::__thrift_field_type!($element_type) > };
580    (binary) => { Vec<u8> };
581    (string) => { String };
582    (double) => { $crate::parquet_thrift::OrderedF64 };
583    ($field_type:ty) => { $field_type };
584}
585
586#[doc(hidden)]
587#[macro_export]
588macro_rules! __thrift_union_type {
589    (binary $lt:lifetime) => { &$lt [u8] };
590    (string $lt:lifetime) => { &$lt str };
591    ($field_type:ident $lt:lifetime) => { $field_type<$lt> };
592    ($field_type:ident) => { $field_type };
593    (list $field_type:ident) => { Vec<$field_type> };
594}
595
596#[doc(hidden)]
597#[macro_export]
598macro_rules! __thrift_read_variant {
599    ($prot:tt, $field_name:ident $field_type:ident) => {
600        Self::$field_name($field_type::read_thrift(&mut *$prot)?)
601    };
602    ($prot:tt, $field_name:ident list $field_type:ident) => {
603        Self::$field_name(Vec::<$field_type>::read_thrift(&mut *$prot)?)
604    };
605    ($prot:tt, $field_name:ident) => {{
606        $prot.skip_empty_struct()?;
607        Self::$field_name
608    }};
609}
610
611#[doc(hidden)]
612#[macro_export]
613macro_rules! __thrift_write_variant_lhs {
614    ($field_name:ident $field_type:ident, $val:tt) => {
615        Self::$field_name($val)
616    };
617    ($field_name:ident, $val:tt) => {
618        Self::$field_name
619    };
620}
621
622#[doc(hidden)]
623#[macro_export]
624macro_rules! __thrift_write_variant_rhs {
625    ($field_id:literal $field_type:ident, $writer:tt, $val:ident) => {
626        $val.write_thrift_field($writer, $field_id, 0)?
627    };
628    ($field_id:literal, $writer:tt, $val:tt) => {
629        $writer.write_empty_struct($field_id, 0)?
630    };
631}