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#![deny(clippy::mod_module_files)]
136
137use darling::FromDeriveInput;
138use quote::{ToTokens, quote};
139use syn::{DeriveInput, parse_macro_input};
140
141/// Derive helper macro for the `isobmff` crate.
142///
143/// See the [crate documentation](crate) for more information on how to use this macro.
144#[proc_macro_derive(IsoBox, attributes(iso_box))]
145pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
146    let derive_input = parse_macro_input!(input);
147
148    match box_impl(derive_input) {
149        Ok(tokens) => tokens.into(),
150        Err(err) => err.to_compile_error().into(),
151    }
152}
153
154#[derive(Debug, darling::FromDeriveInput)]
155#[darling(attributes(iso_box), supports(struct_named))]
156struct IsoBoxOpts {
157    ident: syn::Ident,
158    generics: syn::Generics,
159    data: darling::ast::Data<(), IsoBoxField>,
160    box_type: Option<syn::LitByteStr>,
161    #[darling(default = "default_crate_path")]
162    crate_path: syn::Path,
163    #[darling(default)]
164    skip_impl: Option<SkipImpls>,
165}
166
167fn default_crate_path() -> syn::Path {
168    syn::parse_str("::isobmff").unwrap()
169}
170
171#[derive(Debug)]
172struct SkipImpls(Vec<SkipImpl>);
173
174impl darling::FromMeta for SkipImpls {
175    fn from_list(items: &[darling::ast::NestedMeta]) -> darling::Result<Self> {
176        let skips = items
177            .iter()
178            .map(|m| match m {
179                darling::ast::NestedMeta::Meta(mi) => {
180                    if let Some(ident) = mi.path().get_ident() {
181                        SkipImpl::from_string(ident.to_string().as_str())
182                    } else {
183                        Ok(SkipImpl::All)
184                    }
185                }
186                darling::ast::NestedMeta::Lit(lit) => SkipImpl::from_value(lit),
187            })
188            .collect::<Result<_, _>>()?;
189        Ok(SkipImpls(skips))
190    }
191
192    fn from_word() -> darling::Result<Self> {
193        Ok(SkipImpls(vec![SkipImpl::All]))
194    }
195
196    fn from_string(value: &str) -> darling::Result<Self> {
197        Ok(SkipImpls(vec![SkipImpl::from_string(value)?]))
198    }
199}
200
201impl SkipImpls {
202    fn should_impl(&self, this_impl: SkipImpl) -> bool {
203        if self.0.contains(&SkipImpl::All) {
204            // Nothing should be implemented when all impls are skipped
205            return false;
206        }
207
208        if self.0.contains(&this_impl) {
209            return false;
210        }
211
212        true
213    }
214}
215
216#[derive(Debug, PartialEq, Eq, darling::FromMeta)]
217enum SkipImpl {
218    All,
219    Deserialize,
220    DeserializeSeed,
221    Serialize,
222    Sized,
223    IsoBox,
224}
225
226fn into_fields_checked(data: darling::ast::Data<(), IsoBoxField>) -> syn::Result<darling::ast::Fields<IsoBoxField>> {
227    let fields = data.take_struct().expect("unreachable: only structs supported");
228
229    if let Some(field) = fields.iter().filter(|f| f.repeated).nth(1) {
230        return Err(syn::Error::new_spanned(
231            field.ident.as_ref().expect("unreachable: only named fields supported"),
232            "Only one field can be marked as repeated",
233        ));
234    }
235
236    if let Some(field) = fields.iter().filter(|f| f.repeated).nth(1) {
237        return Err(syn::Error::new_spanned(
238            field.ident.as_ref().expect("unreachable: only named fields supported"),
239            "There can only be one repeated field in the struct",
240        ));
241    }
242
243    if let Some((_, field)) = fields.iter().enumerate().find(|(i, f)| f.repeated && *i != fields.len() - 1) {
244        return Err(syn::Error::new_spanned(
245            field.ident.as_ref().expect("unreachable: only named fields supported"),
246            "Repeated fields must be the last field in the struct",
247        ));
248    }
249
250    if fields.iter().any(|f| f.repeated)
251        && let Some(field) = fields.iter().find(|f| f.nested_box.is_some())
252    {
253        return Err(syn::Error::new_spanned(
254            field.ident.as_ref().expect("unreachable: only named fields supported"),
255            "Cannot combine repeated and nested_box in the same struct",
256        ));
257    }
258
259    Ok(fields)
260}
261
262#[derive(Debug, darling::FromField, Clone)]
263#[darling(attributes(iso_box))]
264struct IsoBoxField {
265    ident: Option<syn::Ident>,
266    ty: syn::Type,
267    #[darling(default)]
268    from: Option<syn::Type>,
269    #[darling(default)]
270    repeated: bool,
271    #[darling(default)]
272    nested_box: Option<IsoBoxFieldNestedBox>,
273}
274
275#[derive(Debug, Default, darling::FromMeta, PartialEq, Eq, Clone, Copy)]
276#[darling(default, from_word = default_field_collect)]
277enum IsoBoxFieldNestedBox {
278    #[default]
279    Single,
280    Collect,
281    CollectUnknown,
282}
283
284fn default_field_collect() -> darling::Result<IsoBoxFieldNestedBox> {
285    Ok(IsoBoxFieldNestedBox::default())
286}
287
288fn box_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
289    let opts = IsoBoxOpts::from_derive_input(&input)?;
290    let crate_path = opts.crate_path;
291
292    let fields = into_fields_checked(opts.data)?;
293
294    let mut fields_in_self = Vec::new();
295    let mut field_parsers = Vec::new();
296    let mut field_serializers = Vec::new();
297
298    for field in fields.iter().filter(|f| f.nested_box.is_none()) {
299        let field_name = field.ident.as_ref().expect("unreachable: only named fields supported");
300
301        let read_field = if field.repeated {
302            read_field_repeated(field, &crate_path)
303        } else if field.from.is_some() {
304            read_field_with_from(field, &crate_path)
305        } else {
306            read_field(field, &crate_path)
307        };
308
309        fields_in_self.push(field_name.to_token_stream());
310        field_parsers.push(quote! {
311            let #field_name = #read_field;
312        });
313
314        match (field.repeated, &field.from) {
315            (true, None) => {
316                field_serializers.push(quote! {
317                    for item in &self.#field_name {
318                        #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(item, &mut writer)?;
319                    }
320                });
321            }
322            (true, Some(from_ty)) => {
323                field_serializers.push(quote! {
324                    for item in &self.#field_name {
325                        #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&::std::convert::Into::<#from_ty>::into(*item), &mut writer)?;
326                    }
327                });
328            }
329            (false, None) => {
330                field_serializers.push(quote! {
331                    #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&self.#field_name, &mut writer)?;
332                });
333            }
334            (false, Some(from_ty)) => {
335                field_serializers.push(quote! {
336                    #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&::std::convert::Into::<#from_ty>::into(self.#field_name), &mut writer)?;
337                });
338            }
339        }
340    }
341
342    let collect_boxes = fields.iter().any(|f| f.nested_box.is_some());
343
344    let box_parser = if collect_boxes {
345        Some(nested_box_parser(fields.iter(), &crate_path))
346    } else {
347        None
348    };
349
350    for (field, nested) in fields.iter().filter_map(|f| f.nested_box.map(|n| (f, n))) {
351        let field_name = field.ident.clone().expect("unreachable: only named fields supported");
352        let field_name_str = field_name.to_string();
353
354        match nested {
355            IsoBoxFieldNestedBox::Single => {
356                fields_in_self.push(quote! {
357                    #field_name: ::std::option::Option::ok_or(#field_name, ::std::io::Error::new(::std::io::ErrorKind::InvalidData, format!("{} not found", #field_name_str)))?
358                });
359                field_serializers.push(quote! {
360                    #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(&self.#field_name, &mut writer)?;
361                });
362            }
363            IsoBoxFieldNestedBox::Collect | IsoBoxFieldNestedBox::CollectUnknown => {
364                fields_in_self.push(field_name.to_token_stream());
365                field_serializers.push(quote! {
366                    #[allow(for_loops_over_fallibles)]
367                    for item in &self.#field_name {
368                        #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize::serialize(item, &mut writer)?;
369                    }
370                });
371            }
372        }
373    }
374
375    let ident = opts.ident;
376    let generics = opts.generics;
377
378    let mut impls = Vec::new();
379
380    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::IsoBox)) {
381        let box_type = opts.box_type.ok_or(syn::Error::new_spanned(
382            &ident,
383            "box_type is required for IsoBox (use skip_impl(iso_box) to skip this impl)",
384        ))?;
385
386        impls.push(quote! {
387            #[automatically_derived]
388            impl #generics IsoBox for #ident #generics {
389                const TYPE: #crate_path::BoxType = #crate_path::BoxType::FourCc(*#box_type);
390            }
391        });
392    }
393
394    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::Deserialize)) {
395        impls.push(quote! {
396            #[automatically_derived]
397            impl<'a> #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize<'a> for #ident #generics {
398                fn deserialize<R>(mut reader: R) -> ::std::io::Result<Self>
399                where
400                    R: #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
401                {
402                    let seed = <#crate_path::BoxHeader as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)?;
403                    if let Some(size) = #crate_path::BoxHeader::payload_size(&seed) {
404                        // Limit the reader when we know the payload size
405                        let reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(reader, size);
406                        <Self as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(reader, seed)
407                    } else {
408                        <Self as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(reader, seed)
409                    }
410                }
411            }
412        });
413    }
414
415    if opts
416        .skip_impl
417        .as_ref()
418        .is_none_or(|s| s.should_impl(SkipImpl::DeserializeSeed))
419    {
420        impls.push(quote! {
421            #[automatically_derived]
422            impl<'a> #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<'a, #crate_path::BoxHeader> for #ident #generics {
423                fn deserialize_seed<R>(mut reader: R, seed: #crate_path::BoxHeader) -> ::std::io::Result<Self>
424                where
425                    R: #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
426                {
427                    #(#field_parsers)*
428                    #box_parser
429
430                    Ok(Self {
431                        #(#fields_in_self,)*
432                    })
433                }
434            }
435        });
436    }
437
438    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::Serialize)) {
439        impls.push(quote! {
440            #[automatically_derived]
441            impl #generics #crate_path::reexports::scuffle_bytes_util::zero_copy::Serialize for #ident #generics {
442                fn serialize<W>(&self, mut writer: W) -> ::std::io::Result<()>
443                where
444                    W: ::std::io::Write
445                {
446                    <Self as #crate_path::IsoBox>::serialize_box_header(self, &mut writer)?;
447                    #(#field_serializers)*
448                    Ok(())
449                }
450            }
451        });
452    }
453
454    if opts.skip_impl.as_ref().is_none_or(|s| s.should_impl(SkipImpl::Sized)) {
455        let field_names = fields
456            .fields
457            .iter()
458            .map(|f| f.ident.clone().expect("unreachable: only named fields supported"))
459            .collect::<Vec<_>>();
460
461        impls.push(quote! {
462            #[automatically_derived]
463            impl #generics #crate_path::IsoSized for #ident #generics {
464                fn size(&self) -> usize {
465                    <Self as #crate_path::IsoBox>::add_header_size(#(#crate_path::IsoSized::size(&self.#field_names))+*)
466                }
467            }
468        });
469    }
470
471    Ok(impls.into_iter().collect())
472}
473
474fn nested_box_parser<'a>(fields: impl Iterator<Item = &'a IsoBoxField>, crate_path: &syn::Path) -> proc_macro2::TokenStream {
475    let mut inits = Vec::new();
476    let mut match_arms = Vec::new();
477    let mut catch_all_arms = Vec::new();
478
479    for (f, nested) in fields.filter_map(|f| f.nested_box.as_ref().map(|n| (f, n))) {
480        let field_type = &f.ty;
481        let field_name = f.ident.as_ref().expect("unreachable: only named fields supported");
482
483        match nested {
484            IsoBoxFieldNestedBox::Single => {
485                inits.push(quote! {
486                    let mut #field_name = ::std::option::Option::None;
487                });
488                match_arms.push(quote! {
489                    <#field_type as #crate_path::IsoBox>::TYPE => {
490                        if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
491                            // Initialize the payload reader with the payload size
492                            let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
493                            // Deserialize the box payload
494                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
495                                <#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(
496                                    &mut payload_reader,
497                                    box_header,
498                                )
499                            )? else {
500                                // EOF
501                                // Align the reader to the start of the next box
502                                #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
503                                break;
504                            };
505                            // Align the reader to the start of the next box
506                            #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
507                            #field_name = ::std::option::Option::Some(iso_box);
508                        } else {
509                            // Deserialize the box payload
510                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
511                                <#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<#crate_path::BoxHeader>>::deserialize_seed(
512                                    &mut reader,
513                                    box_header,
514                                )
515                            )? else {
516                                // EOF
517                                break;
518                            };
519                            #field_name = ::std::option::Option::Some(iso_box);
520                        }
521                    }
522                });
523            }
524            IsoBoxFieldNestedBox::Collect => {
525                inits.push(quote! {
526                    let mut #field_name = <#field_type as ::std::default::Default>::default();
527                });
528                match_arms.push(quote! {
529                    <<#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Container>::Item as #crate_path::IsoBox>::TYPE => {
530                        if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
531                            // Initialize the payload reader with the payload size
532                            let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
533                            // Deserialize the box payload
534                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
535                                <<#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(
536                                    &mut payload_reader,
537                                    box_header,
538                                )
539                            )? else {
540                                // EOF
541                                // Align the reader to the start of the next box
542                                #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
543                                break;
544                            };
545                            // Align the reader to the start of the next box
546                            #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
547                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, iso_box);
548                        } else {
549                            // Deserialize the box payload
550                            let Some(iso_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
551                                <<#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(
552                                    &mut reader,
553                                    box_header,
554                                )
555                            )? else {
556                                // EOF
557                                break;
558                            };
559                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, iso_box);
560                        }
561                    }
562                });
563            }
564            IsoBoxFieldNestedBox::CollectUnknown => {
565                inits.push(quote! {
566                    let mut #field_name = <#field_type as ::std::default::Default>::default();
567                });
568                catch_all_arms.push(quote! {
569                    _ => {
570                        if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
571                            let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
572                            let Some(unknown_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
573                                <#crate_path::UnknownBox as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<'_, #crate_path::BoxHeader>>::deserialize_seed(&mut payload_reader, box_header)
574                            )? else {
575                                break;
576                            };
577                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, unknown_box);
578                            // Align the reader to the start of the next box
579                            #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
580                        } else {
581                            let Some(unknown_box) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
582                                <#crate_path::UnknownBox as #crate_path::reexports::scuffle_bytes_util::zero_copy::DeserializeSeed<'_, #crate_path::BoxHeader>>::deserialize_seed(&mut reader, box_header)
583                            )? else {
584                                break;
585                            };
586                            #crate_path::reexports::scuffle_bytes_util::zero_copy::Container::add(&mut #field_name, unknown_box);
587                        }
588
589                    }
590                });
591            }
592        }
593    }
594
595    quote! {
596        #(#inits)*
597        loop {
598            // Deserialize the box header which is part of every box
599            let Some(box_header) = #crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
600                <#crate_path::BoxHeader as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)
601            )? else {
602                // EOF
603                break;
604            };
605
606            match box_header.box_type {
607                #(#match_arms)*
608                #(#catch_all_arms)*
609                _ => {
610                    // Ignore unknown boxes if we are not collecting them
611                    // Align the reader to the start of the next box
612                    if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&box_header) {
613                        let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
614                        #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut payload_reader)?;
615                    } else {
616                        #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::try_read_to_end(&mut reader)?;
617                    }
618                }
619            }
620        }
621    }
622}
623
624fn read_field_repeated(field: &IsoBoxField, crate_path: &syn::Path) -> proc_macro2::TokenStream {
625    let field_type = &field.ty;
626
627    if let Some(from) = field.from.as_ref() {
628        quote! {
629            {
630                if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&seed) {
631                    let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
632                    let iter = ::std::iter::from_fn(||
633                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
634                            <#from as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut payload_reader)
635                        ))
636                    );
637                    let iter = ::std::iter::Iterator::map(iter, |item| {
638                        match item {
639                            Ok(item) => Ok(::std::convert::From::from(item)),
640                            Err(e) => Err(e),
641                        }
642                    });
643                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
644                } else {
645                    let iter = ::std::iter::from_fn(||
646                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
647                            <#from as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)
648                        ))
649                    );
650                    let iter = ::std::iter::Iterator::map(iter, |item| {
651                        match item {
652                            Ok(item) => Ok(::std::convert::From::from(item)),
653                            Err(e) => Err(e),
654                        }
655                    });
656                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
657                }
658            }
659        }
660    } else {
661        quote! {
662            {
663                if let Some(payload_size) = #crate_path::BoxHeader::payload_size(&seed) {
664                    let mut payload_reader = #crate_path::reexports::scuffle_bytes_util::zero_copy::ZeroCopyReader::take(&mut reader, payload_size);
665                    let iter = ::std::iter::from_fn(||
666                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
667                            <<#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)
668                        ))
669                    );
670                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
671                } else {
672                    let iter = ::std::iter::from_fn(||
673                        ::std::result::Result::transpose(#crate_path::reexports::scuffle_bytes_util::IoResultExt::eof_to_none(
674                            <<#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)
675                        ))
676                    );
677                    ::std::iter::Iterator::collect::<::std::result::Result<#field_type, ::std::io::Error>>(iter)?
678                }
679            }
680        }
681    }
682}
683
684fn read_field_with_from(field: &IsoBoxField, crate_path: &syn::Path) -> proc_macro2::TokenStream {
685    let field_type = &field.ty;
686    let read_field = read_field(field, crate_path);
687
688    quote! {
689        <#field_type as ::std::convert::From<_>>::from(#read_field)
690    }
691}
692
693fn read_field(field: &IsoBoxField, crate_path: &syn::Path) -> proc_macro2::TokenStream {
694    let field_type = field.from.as_ref().unwrap_or(&field.ty);
695
696    quote! {
697        <#field_type as #crate_path::reexports::scuffle_bytes_util::zero_copy::Deserialize>::deserialize(&mut reader)?
698    }
699}