isobmff_derive/
lib.rs

1//! Derive helper macro for the `isobmff` crate.
2//!
3//! Use this macro to implement the `IsoBox` trait as well as the resptive implementations of the
4//! `Deserialize`, `DeserializeSeed`, `Serialize`, and `IsoSized` traits.
5//!
6//! ## Usage
7//!
8//! This derive macro can only be used on structs with named fields.
9//!
10//! All field types must implement the `Deserialize` and `Serialize` traits from the `scuffle_bytes_util` crate.
11//! If that cannot be guaranteed, you should use the `from` field attribute. (See below)
12//!
13//! ### Struct Attributes
14//!
15//! | Attribute | Description | Required |
16//! |---|---|---|
17//! | `box_type` | The FourCC box type of the box. Provide as a byte array of size 4 (`[u8; 4]`). | Yes |
18//! | `crate_path` | The path to the `isobmff` crate. Defaults to `::isobmff`. | No |
19//! | `skip_impl` | A list of impls that should be skipped by the code generation. (i.e. you want to implement them manually). Defaults to none. | No |
20//!
21//! ### Field Attributes
22//!
23//! | Attribute | Description | Required |
24//! |---|---|---|
25//! | `from` | If specified, the provided type is parsed and then converted to the expected type using the [`From`] trait. | No |
26//! | `repeated` | Repeted fields are read repeatedly until the reader reaches EOF. There can only be one repeated field which should also appear as the last field in the struct. | No |
27//! | `nested_box` | Can be used to make other boxes part of the box. The reader will read all boxes after the actual payload (the other fields) was read. Use `nested_box(collect)` to read optional/multiple boxes. Use `nested_box(collect_unknown)` to capture any unknown boxes. | No |
28//!
29//! ## Example
30//!
31//! ```rust
32//! use isobmff::IsoBox;
33//!
34//! #[derive(IsoBox)]
35//! #[iso_box(box_type = b"myb1")]
36//! pub struct MyCustomBox {
37//!     pub foo: u32,
38//!     pub bar: u8,
39//!     #[iso_box(repeated)]
40//!     pub baz: Vec<i16>,
41//! }
42//! ```
43//!
44//! The macro will generate code equivalent to this:
45//!
46//! ```rust
47//! use isobmff::{BoxHeader, BoxType, IsoBox, IsoSized};
48//! use scuffle_bytes_util::IoResultExt;
49//! use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, ZeroCopyReader};
50//! # pub struct MyCustomBox {
51//! #     pub foo: u32,
52//! #     pub bar: u8,
53//! #     pub baz: Vec<i16>,
54//! # }
55//!
56//! impl IsoBox for MyCustomBox {
57//!     const TYPE: BoxType = BoxType::FourCc(*b"myb1");
58//! }
59//!
60//! impl<'a> Deserialize<'a> for MyCustomBox {
61//!     fn deserialize<R>(mut reader: R) -> std::io::Result<Self>
62//!     where
63//!         R: ZeroCopyReader<'a>,
64//!     {
65//!         let seed = BoxHeader::deserialize(&mut reader)?;
66//!
67//!         if let Some(size) = BoxHeader::payload_size(&seed) {
68//!             Self::deserialize_seed(reader.take(size), seed)
69//!         } else {
70//!             Self::deserialize_seed(reader, seed)
71//!         }
72//!     }
73//! }
74//!
75//! impl<'a> DeserializeSeed<'a, BoxHeader> for MyCustomBox {
76//!     fn deserialize_seed<R>(mut reader: R, seed: BoxHeader) -> std::io::Result<Self>
77//!     where
78//!         R: ZeroCopyReader<'a>,
79//!     {
80//!         let foo = u32::deserialize(&mut reader)?;
81//!         let bar = u8::deserialize(&mut reader)?;
82//!
83//!         let baz = {
84//!             if let Some(payload_size) = seed.payload_size() {
85//!                 let mut payload_reader = reader.take(payload_size);
86//!                 std::iter::from_fn(|| {
87//!                     i16::deserialize(&mut payload_reader).eof_to_none().transpose()
88//!                 }).collect::<Result<Vec<_>, std::io::Error>>()?
89//!             } else {
90//!                 std::iter::from_fn(|| {
91//!                     i16::deserialize(&mut reader).eof_to_none().transpose()
92//!                 }).collect::<Result<Vec<_>, std::io::Error>>()?
93//!             }
94//!         };
95//!
96//!         Ok(Self { foo, bar, baz })
97//!     }
98//! }
99//!
100//! impl Serialize for MyCustomBox {
101//!     fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
102//!     where
103//!         W: std::io::Write,
104//!     {
105//!         self.serialize_box_header(&mut writer)?;
106//!
107//!         self.foo.serialize(&mut writer)?;
108//!         self.bar.serialize(&mut writer)?;
109//!         for item in &self.baz {
110//!             item.serialize(&mut writer)?;
111//!         }
112//!
113//!         Ok(())
114//!     }
115//! }
116//!
117//! impl IsoSized for MyCustomBox {
118//!     fn size(&self) -> usize {
119//!         Self::add_header_size(self.foo.size() + self.bar.size() + self.baz.size())
120//!     }
121//! }
122//! ```
123//!
124//! ## License
125//!
126//! This project is licensed under the MIT or Apache-2.0 license.
127//! You can choose between one of them if you use this work.
128//!
129//! `SPDX-License-Identifier: MIT OR Apache-2.0`
130#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
131#![cfg_attr(docsrs, feature(doc_auto_cfg))]
132#![deny(missing_docs)]
133#![deny(unsafe_code)]
134#![deny(unreachable_pub)]
135
136use darling::FromDeriveInput;
137use quote::{ToTokens, quote};
138use syn::{DeriveInput, parse_macro_input};
139
140/// Derive helper macro for the `isobmff` crate.
141///
142/// See the [crate documentation](crate) for more information on how to use this macro.
143#[proc_macro_derive(IsoBox, attributes(iso_box))]
144pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
145    let derive_input = parse_macro_input!(input);
146
147    match box_impl(derive_input) {
148        Ok(tokens) => tokens.into(),
149        Err(err) => err.to_compile_error().into(),
150    }
151}
152
153#[derive(Debug, darling::FromDeriveInput)]
154#[darling(attributes(iso_box), supports(struct_named))]
155struct IsoBoxOpts {
156    ident: syn::Ident,
157    generics: syn::Generics,
158    data: darling::ast::Data<(), IsoBoxField>,
159    box_type: Option<syn::LitByteStr>,
160    #[darling(default = "default_crate_path")]
161    crate_path: syn::Path,
162    #[darling(default)]
163    skip_impl: Option<SkipImpls>,
164}
165
166fn default_crate_path() -> syn::Path {
167    syn::parse_str("::isobmff").unwrap()
168}
169
170#[derive(Debug)]
171struct SkipImpls(Vec<SkipImpl>);
172
173impl darling::FromMeta for SkipImpls {
174    fn from_list(items: &[darling::ast::NestedMeta]) -> darling::Result<Self> {
175        let skips = items
176            .iter()
177            .map(|m| match m {
178                darling::ast::NestedMeta::Meta(mi) => {
179                    if let Some(ident) = mi.path().get_ident() {
180                        SkipImpl::from_string(ident.to_string().as_str())
181                    } else {
182                        Ok(SkipImpl::All)
183                    }
184                }
185                darling::ast::NestedMeta::Lit(lit) => SkipImpl::from_value(lit),
186            })
187            .collect::<Result<_, _>>()?;
188        Ok(SkipImpls(skips))
189    }
190
191    fn from_word() -> darling::Result<Self> {
192        Ok(SkipImpls(vec![SkipImpl::All]))
193    }
194
195    fn from_string(value: &str) -> darling::Result<Self> {
196        Ok(SkipImpls(vec![SkipImpl::from_string(value)?]))
197    }
198}
199
200impl SkipImpls {
201    fn should_impl(&self, this_impl: SkipImpl) -> bool {
202        if self.0.contains(&SkipImpl::All) {
203            // Nothing should be implemented when all impls are skipped
204            return false;
205        }
206
207        if self.0.contains(&this_impl) {
208            return false;
209        }
210
211        true
212    }
213}
214
215#[derive(Debug, PartialEq, Eq, darling::FromMeta)]
216enum SkipImpl {
217    All,
218    Deserialize,
219    DeserializeSeed,
220    Serialize,
221    Sized,
222    IsoBox,
223}
224
225fn into_fields_checked(data: darling::ast::Data<(), IsoBoxField>) -> syn::Result<darling::ast::Fields<IsoBoxField>> {
226    let fields = data.take_struct().expect("unreachable: only structs supported");
227
228    if let Some(field) = fields.iter().filter(|f| f.repeated).nth(1) {
229        return Err(syn::Error::new_spanned(
230            field.ident.as_ref().expect("unreachable: only named fields supported"),
231            "Only one field can be marked as repeated",
232        ));
233    }
234
235    if let Some(field) = fields.iter().filter(|f| f.repeated).nth(1) {
236        return Err(syn::Error::new_spanned(
237            field.ident.as_ref().expect("unreachable: only named fields supported"),
238            "There can only be one repeated field in the struct",
239        ));
240    }
241
242    if let Some((_, field)) = fields.iter().enumerate().find(|(i, f)| f.repeated && *i != fields.len() - 1) {
243        return Err(syn::Error::new_spanned(
244            field.ident.as_ref().expect("unreachable: only named fields supported"),
245            "Repeated fields must be the last field in the struct",
246        ));
247    }
248
249    if fields.iter().any(|f| f.repeated) {
250        if let Some(field) = fields.iter().find(|f| f.nested_box.is_some()) {
251            return Err(syn::Error::new_spanned(
252                field.ident.as_ref().expect("unreachable: only named fields supported"),
253                "Cannot combine repeated and nested_box in the same struct",
254            ));
255        }
256    }
257
258    Ok(fields)
259}
260
261#[derive(Debug, darling::FromField, Clone)]
262#[darling(attributes(iso_box))]
263struct IsoBoxField {
264    ident: Option<syn::Ident>,
265    ty: syn::Type,
266    #[darling(default)]
267    from: Option<syn::Type>,
268    #[darling(default)]
269    repeated: bool,
270    #[darling(default)]
271    nested_box: Option<IsoBoxFieldNestedBox>,
272}
273
274#[derive(Debug, Default, darling::FromMeta, PartialEq, Eq, Clone, Copy)]
275#[darling(default, from_word = default_field_collect)]
276enum IsoBoxFieldNestedBox {
277    #[default]
278    Single,
279    Collect,
280    CollectUnknown,
281}
282
283fn default_field_collect() -> darling::Result<IsoBoxFieldNestedBox> {
284    Ok(IsoBoxFieldNestedBox::default())
285}
286
287fn box_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
288    let opts = IsoBoxOpts::from_derive_input(&input)?;
289    let crate_path = opts.crate_path;
290
291    let fields = into_fields_checked(opts.data)?;
292
293    let mut fields_in_self = Vec::new();
294    let mut field_parsers = Vec::new();
295    let mut field_serializers = Vec::new();
296
297    for field in fields.iter().filter(|f| f.nested_box.is_none()) {
298        let field_name = field.ident.as_ref().expect("unreachable: only named fields supported");
299
300        let read_field = if field.repeated {
301            read_field_repeated(field, &crate_path)
302        } else if field.from.is_some() {
303            read_field_with_from(field, &crate_path)
304        } else {
305            read_field(field, &crate_path)
306        };
307
308        fields_in_self.push(field_name.to_token_stream());
309        field_parsers.push(quote! {
310            let #field_name = #read_field;
311        });
312
313        match (field.repeated, &field.from) {
314            (true, None) => {
315                field_serializers.push(quote! {
316                    for item in &self.#field_name {
317                        #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(item, &mut writer)?;
318                    }
319                });
320            }
321            (true, Some(from_ty)) => {
322                field_serializers.push(quote! {
323                    for item in &self.#field_name {
324                        #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&::std::convert::Into::<#from_ty>::into(*item), &mut writer)?;
325                    }
326                });
327            }
328            (false, None) => {
329                field_serializers.push(quote! {
330                    #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&self.#field_name, &mut writer)?;
331                });
332            }
333            (false, Some(from_ty)) => {
334                field_serializers.push(quote! {
335                    #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&::std::convert::Into::<#from_ty>::into(self.#field_name), &mut writer)?;
336                });
337            }
338        }
339    }
340
341    let collect_boxes = fields.iter().any(|f| f.nested_box.is_some());
342
343    let box_parser = if collect_boxes {
344        Some(nested_box_parser(fields.iter(), &crate_path))
345    } else {
346        None
347    };
348
349    for (field, nested) in fields.iter().filter_map(|f| f.nested_box.map(|n| (f, n))) {
350        let field_name = field.ident.clone().expect("unreachable: only named fields supported");
351        let field_name_str = field_name.to_string();
352
353        match nested {
354            IsoBoxFieldNestedBox::Single => {
355                fields_in_self.push(quote! {
356                    #field_name: ::std::option::Option::ok_or(#field_name, ::std::io::Error::new(::std::io::ErrorKind::InvalidData, format!("{} not found", #field_name_str)))?
357                });
358                field_serializers.push(quote! {
359                    #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&self.#field_name, &mut writer)?;
360                });
361            }
362            IsoBoxFieldNestedBox::Collect | IsoBoxFieldNestedBox::CollectUnknown => {
363                fields_in_self.push(field_name.to_token_stream());
364                field_serializers.push(quote! {
365                    #[allow(for_loops_over_fallibles)]
366                    for item in &self.#field_name {
367                        #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(item, &mut writer)?;
368                    }
369                });
370            }
371        }
372    }
373
374    let ident = opts.ident;
375    let generics = opts.generics;
376
377    let mut impls = Vec::new();
378
379    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::IsoBox)) {
380        let box_type = opts.box_type.ok_or(syn::Error::new_spanned(
381            &ident,
382            "box_type is required for IsoBox (use skip_impl(iso_box) to skip this impl)",
383        ))?;
384
385        impls.push(quote! {
386            #[automatically_derived]
387            impl #generics IsoBox for #ident #generics {
388                const TYPE: #crate_path::BoxType = #crate_path::BoxType::FourCc(*#box_type);
389            }
390        });
391    }
392
393    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::Deserialize)) {
394        impls.push(quote! {
395            #[automatically_derived]
396            impl<'a> #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize<'a> for #ident #generics {
397                fn deserialize<R>(mut reader: R) -> ::std::io::Result<Self>
398                where
399                    R: #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
400                {
401                    let seed = <#crate_path::BoxHeader as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)?;
402                    if let Some(size) = #crate_path::BoxHeader::payload_size(&seed) {
403                        // Limit the reader when we know the payload size
404                        let reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(reader, size);
405                        <Self as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(reader, seed)
406                    } else {
407                        <Self as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(reader, seed)
408                    }
409                }
410            }
411        });
412    }
413
414    if opts
415        .skip_impl
416        .as_ref()
417        .is_none_or(|s| s.should_impl(SkipImpl::DeserializeSeed))
418    {
419        impls.push(quote! {
420            #[automatically_derived]
421            impl<'a> #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<'a, #crate_path::BoxHeader> for #ident #generics {
422                fn deserialize_seed<R>(mut reader: R, seed: #crate_path::BoxHeader) -> ::std::io::Result<Self>
423                where
424                    R: #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
425                {
426                    #(#field_parsers)*
427                    #box_parser
428
429                    Ok(Self {
430                        #(#fields_in_self,)*
431                    })
432                }
433            }
434        });
435    }
436
437    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::Serialize)) {
438        impls.push(quote! {
439            #[automatically_derived]
440            impl #generics #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize for #ident #generics {
441                fn serialize<W>(&self, mut writer: W) -> ::std::io::Result<()>
442                where
443                    W: ::std::io::Write
444                {
445                    <Self as #crate_path::IsoBox>::serialize_box_header(self, &mut writer)?;
446                    #(#field_serializers)*
447                    Ok(())
448                }
449            }
450        });
451    }
452
453    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::Sized)) {
454        let field_names = fields
455            .fields
456            .iter()
457            .map(|f| f.ident.clone().expect("unreachable: only named fields supported"))
458            .collect::<Vec<_>>();
459
460        impls.push(quote! {
461            #[automatically_derived]
462            impl #generics #crate_path::IsoSized for #ident #generics {
463                fn size(&self) -> usize {
464                    <Self as #crate_path::IsoBox>::add_header_size(#(#crate_path::IsoSized::size(&self.#field_names))+*)
465                }
466            }
467        });
468    }
469
470    Ok(impls.into_iter().collect())
471}
472
473fn nested_box_parser<'a>(fields: impl Iterator<Item = &'a IsoBoxField>, crate_path: &syn::Path) -> proc_macro2::TokenStream {
474    let mut inits = Vec::new();
475    let mut match_arms = Vec::new();
476    let mut catch_all_arms = Vec::new();
477
478    for (f, nested) in fields.filter_map(|f| f.nested_box.as_ref().map(|n| (f, n))) {
479        let field_type = &f.ty;
480        let field_name = f.ident.as_ref().expect("unreachable: only named fields supported");
481
482        match nested {
483            IsoBoxFieldNestedBox::Single => {
484                inits.push(quote! {
485                    let mut #field_name = ::std::option::Option::None;
486                });
487                match_arms.push(quote! {
488                    <#field_type as #crate_path::IsoBox>::TYPE => {
489                        if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
490                            // Initialize the payload reader with the payload size
491                            let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
492                            // Deserialize the box payload
493                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
494                                <#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(
495                                    &mut payload_reader,
496                                    box_header,
497                                )
498                            )? else {
499                                // EOF
500                                // Align the reader to the start of the next box
501                                #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
502                                break;
503                            };
504                            // Align the reader to the start of the next box
505                            #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
506                            #field_name = ::std::option::Option::Some(iso_box);
507                        } else {
508                            // Deserialize the box payload
509                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
510                                <#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(
511                                    &mut reader,
512                                    box_header,
513                                )
514                            )? else {
515                                // EOF
516                                break;
517                            };
518                            #field_name = ::std::option::Option::Some(iso_box);
519                        }
520                    }
521                });
522            }
523            IsoBoxFieldNestedBox::Collect => {
524                inits.push(quote! {
525                    let mut #field_name = <#field_type as ::std::default::Default>::default();
526                });
527                match_arms.push(quote! {
528                    <<#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Container>::Item as #crate_path::IsoBox>::TYPE => {
529                        if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
530                            // Initialize the payload reader with the payload size
531                            let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
532                            // Deserialize the box payload
533                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
534                                <<#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Container>::Item as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(
535                                    &mut payload_reader,
536                                    box_header,
537                                )
538                            )? else {
539                                // EOF
540                                // Align the reader to the start of the next box
541                                #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
542                                break;
543                            };
544                            // Align the reader to the start of the next box
545                            #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
546                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, iso_box);
547                        } else {
548                            // Deserialize the box payload
549                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
550                                <<#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Container>::Item as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(
551                                    &mut reader,
552                                    box_header,
553                                )
554                            )? else {
555                                // EOF
556                                break;
557                            };
558                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, iso_box);
559                        }
560                    }
561                });
562            }
563            IsoBoxFieldNestedBox::CollectUnknown => {
564                inits.push(quote! {
565                    let mut #field_name = <#field_type as ::std::default::Default>::default();
566                });
567                catch_all_arms.push(quote! {
568                    _ => {
569                        if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
570                            let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
571                            let Some(unknown_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
572                                <#crate_path::UnknownBox as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<'_, #crate_path::BoxHeader>>::deserialize_seed(&mut payload_reader, box_header)
573                            )? else {
574                                break;
575                            };
576                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, unknown_box);
577                            // Align the reader to the start of the next box
578                            #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
579                        } else {
580                            let Some(unknown_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
581                                <#crate_path::UnknownBox as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<'_, #crate_path::BoxHeader>>::deserialize_seed(&mut reader, box_header)
582                            )? else {
583                                break;
584                            };
585                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, unknown_box);
586                        }
587
588                    }
589                });
590            }
591        }
592    }
593
594    quote! {
595        #(#inits)*
596        loop {
597            // Deserialize the box header which is part of every box
598            let Some(box_header) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
599                <#crate_path::BoxHeader as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)
600            )? else {
601                // EOF
602                break;
603            };
604
605            match box_header.box_type {
606                #(#match_arms)*
607                #(#catch_all_arms)*
608                _ => {
609                    // Ignore unknown boxes if we are not collecting them
610                    // Align the reader to the start of the next box
611                    if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
612                        let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
613                        #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
614                    } else {
615                        #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut reader)?;
616                    }
617                }
618            }
619        }
620    }
621}
622
623fn read_field_repeated(field: &IsoBoxField, crate_path: &syn::Path) -> proc_macro2::TokenStream {
624    let field_type = &field.ty;
625
626    if let Some(from) = field.from.as_ref() {
627        quote! {
628            {
629                if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&seed) {
630                    let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
631                    let iter = ::std::iter::from_fn(||
632                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
633                            <#from as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut payload_reader)
634                        ))
635                    );
636                    let iter = ::std::iter::Iterator::map(iter, |item| {
637                        match item {
638                            Ok(item) => Ok(::std::convert::From::from(item)),
639                            Err(e) => Err(e),
640                        }
641                    });
642                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
643                } else {
644                    let iter = ::std::iter::from_fn(||
645                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
646                            <#from as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)
647                        ))
648                    );
649                    let iter = ::std::iter::Iterator::map(iter, |item| {
650                        match item {
651                            Ok(item) => Ok(::std::convert::From::from(item)),
652                            Err(e) => Err(e),
653                        }
654                    });
655                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
656                }
657            }
658        }
659    } else {
660        quote! {
661            {
662                if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&seed) {
663                    let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
664                    let iter = ::std::iter::from_fn(||
665                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
666                            <<#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Container>::Item as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut payload_reader)
667                        ))
668                    );
669                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
670                } else {
671                    let iter = ::std::iter::from_fn(||
672                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
673                            <<#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Container>::Item as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)
674                        ))
675                    );
676                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
677                }
678            }
679        }
680    }
681}
682
683fn read_field_with_from(field: &IsoBoxField, crate_path: &syn::Path) -> proc_macro2::TokenStream {
684    let field_type = &field.ty;
685    let read_field = read_field(field, crate_path);
686
687    quote! {
688        <#field_type as ::std::convert::From<_>>::from(#read_field)
689    }
690}
691
692fn read_field(field: &IsoBoxField, crate_path: &syn::Path) -> proc_macro2::TokenStream {
693    let field_type = field.from.as_ref().unwrap_or(&field.ty);
694
695    quote! {
696        <#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)?
697    }
698}