pdl_compiler/backends/rust_legacy/
mod.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Rust compiler backend.
16
17use crate::{analyzer, ast};
18use quote::{format_ident, quote};
19use std::collections::BTreeSet;
20use std::collections::HashMap;
21use std::path::Path;
22use syn::LitInt;
23
24mod parser;
25mod preamble;
26mod serializer;
27pub mod test;
28mod types;
29
30use parser::FieldParser;
31use serializer::FieldSerializer;
32
33pub use heck::ToUpperCamelCase;
34
35pub trait ToIdent {
36    /// Generate a sanitized rust identifier.
37    /// Rust specific keywords are renamed for validity.
38    fn to_ident(self) -> proc_macro2::Ident;
39}
40
41impl ToIdent for &'_ str {
42    fn to_ident(self) -> proc_macro2::Ident {
43        match self {
44            "as" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern"
45            | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod"
46            | "move" | "mut" | "pub" | "ref" | "return" | "self" | "Self" | "static" | "struct"
47            | "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" | "while"
48            | "async" | "await" | "dyn" | "abstract" | "become" | "box" | "do" | "final"
49            | "macro" | "override" | "priv" | "typeof" | "unsized" | "virtual" | "yield"
50            | "try" => format_ident!("r#{}", self),
51            _ => format_ident!("{}", self),
52        }
53    }
54}
55
56/// Generate a bit-mask which masks out `n` least significant bits.
57///
58/// Literal integers in Rust default to the `i32` type. For this
59/// reason, if `n` is larger than 31, a suffix is added to the
60/// `LitInt` returned. This should either be `u64` or `usize`
61/// depending on where the result is used.
62pub fn mask_bits(n: usize, suffix: &str) -> syn::LitInt {
63    let suffix = if n > 31 { format!("_{suffix}") } else { String::new() };
64    // Format the hex digits as 0x1111_2222_3333_usize.
65    let hex_digits = format!("{:x}", (1u64 << n) - 1)
66        .as_bytes()
67        .rchunks(4)
68        .rev()
69        .map(|chunk| std::str::from_utf8(chunk).unwrap())
70        .collect::<Vec<&str>>()
71        .join("_");
72    syn::parse_str::<syn::LitInt>(&format!("0x{hex_digits}{suffix}")).unwrap()
73}
74
75fn generate_packet_size_getter<'a>(
76    scope: &analyzer::Scope<'a>,
77    schema: &analyzer::Schema,
78    fields: impl Iterator<Item = &'a ast::Field>,
79    is_packet: bool,
80) -> (usize, proc_macro2::TokenStream) {
81    let mut constant_width = 0;
82    let mut dynamic_widths = Vec::new();
83
84    for field in fields {
85        if let Some(width) =
86            schema.padded_size(field.key).or(schema.field_size(field.key).static_())
87        {
88            constant_width += width;
89            continue;
90        }
91
92        let decl = scope.get_type_declaration(field);
93        dynamic_widths.push(match &field.desc {
94            ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body { .. } => {
95                if is_packet {
96                    quote! {
97                        self.child.get_total_size()
98                    }
99                } else {
100                    quote! {
101                        self.payload.len()
102                    }
103                }
104            }
105            ast::FieldDesc::Scalar { id, width } => {
106                assert!(field.cond.is_some());
107                let id = id.to_ident();
108                let width = syn::Index::from(*width / 8);
109                quote!(if self.#id.is_some() { #width } else { 0 })
110            }
111            ast::FieldDesc::Typedef { id, type_id, .. } if field.cond.is_some() => {
112                let id = id.to_ident();
113                match &scope.typedef[type_id].desc {
114                    ast::DeclDesc::Enum { width, .. } => {
115                        let width = syn::Index::from(*width / 8);
116                        quote!(if self.#id.is_some() { #width } else { 0 })
117                    }
118                    _ => {
119                        let type_id = type_id.to_ident();
120                        quote! {
121                            self.#id
122                                .as_ref()
123                                .map(#type_id::get_size)
124                                .unwrap_or(0)
125                        }
126                    }
127                }
128            }
129            ast::FieldDesc::Typedef { id, .. } => {
130                let id = id.to_ident();
131                quote!(self.#id.get_size())
132            }
133            ast::FieldDesc::Array { id, width, .. } => {
134                let id = id.to_ident();
135                match &decl {
136                    Some(ast::Decl {
137                        desc: ast::DeclDesc::Struct { .. } | ast::DeclDesc::CustomField { .. },
138                        ..
139                    }) => {
140                        quote! {
141                            self.#id.iter().map(|elem| elem.get_size()).sum::<usize>()
142                        }
143                    }
144                    Some(ast::Decl { desc: ast::DeclDesc::Enum { width, .. }, .. }) => {
145                        let width = syn::Index::from(width / 8);
146                        let mul_width = (width.index > 1).then(|| quote!(* #width));
147                        quote! {
148                            self.#id.len() #mul_width
149                        }
150                    }
151                    _ => {
152                        let width = syn::Index::from(width.unwrap() / 8);
153                        let mul_width = (width.index > 1).then(|| quote!(* #width));
154                        quote! {
155                            self.#id.len() #mul_width
156                        }
157                    }
158                }
159            }
160            _ => panic!("Unsupported field type: {field:?}"),
161        });
162    }
163
164    if constant_width > 0 {
165        let width = syn::Index::from(constant_width / 8);
166        dynamic_widths.insert(0, quote!(#width));
167    }
168    if dynamic_widths.is_empty() {
169        dynamic_widths.push(quote!(0))
170    }
171
172    (
173        constant_width,
174        quote! {
175            #(#dynamic_widths)+*
176        },
177    )
178}
179
180fn top_level_packet<'a>(scope: &analyzer::Scope<'a>, packet_name: &'a str) -> &'a ast::Decl {
181    let mut decl = scope.typedef[packet_name];
182    while let ast::DeclDesc::Packet { parent_id: Some(parent_id), .. }
183    | ast::DeclDesc::Struct { parent_id: Some(parent_id), .. } = &decl.desc
184    {
185        decl = scope.typedef[parent_id];
186    }
187    decl
188}
189
190/// Find parent fields which are constrained in child packets.
191///
192/// These fields are the fields which need to be passed in when
193/// parsing a `id` packet since their values are needed for one or
194/// more child packets.
195fn find_constrained_parent_fields<'a>(
196    scope: &analyzer::Scope<'a>,
197    id: &str,
198) -> Vec<&'a ast::Field> {
199    let all_parent_fields: HashMap<String, &'a ast::Field> = HashMap::from_iter(
200        scope
201            .iter_parent_fields(scope.typedef[id])
202            .filter_map(|f| f.id().map(|id| (id.to_string(), f))),
203    );
204
205    let mut fields = Vec::new();
206    let mut field_names = BTreeSet::new();
207    let mut children = scope.iter_children(scope.typedef[id]).collect::<Vec<_>>();
208
209    while let Some(child) = children.pop() {
210        if let ast::DeclDesc::Packet { id, constraints, .. }
211        | ast::DeclDesc::Struct { id, constraints, .. } = &child.desc
212        {
213            for constraint in constraints {
214                if field_names.insert(&constraint.id)
215                    && all_parent_fields.contains_key(&constraint.id)
216                {
217                    fields.push(all_parent_fields[&constraint.id]);
218                }
219            }
220            children.extend(scope.iter_children(scope.typedef[id]).collect::<Vec<_>>());
221        }
222    }
223
224    fields
225}
226
227/// Generate the declaration and implementation for a data struct.
228///
229/// This struct will hold the data for a packet or a struct. It knows
230/// how to parse and serialize its own fields.
231fn generate_data_struct(
232    scope: &analyzer::Scope<'_>,
233    schema: &analyzer::Schema,
234    endianness: ast::EndiannessValue,
235    id: &str,
236) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
237    let decl = scope.typedef[id];
238    let is_packet = matches!(&decl.desc, ast::DeclDesc::Packet { .. });
239
240    let span = format_ident!("bytes");
241    let serializer_span = format_ident!("buffer");
242    let mut field_parser = FieldParser::new(scope, schema, endianness, id, &span);
243    let mut field_serializer =
244        FieldSerializer::new(scope, schema, endianness, id, &serializer_span);
245    for field in decl.fields() {
246        field_parser.add(field);
247        field_serializer.add(field);
248    }
249    field_parser.done();
250
251    let (parse_arg_names, parse_arg_types) = if is_packet {
252        let fields = find_constrained_parent_fields(scope, id);
253        let names = fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
254        let types = fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>();
255        (names, types)
256    } else {
257        (Vec::new(), Vec::new()) // No extra arguments to parse in structs.
258    };
259
260    let (constant_width, packet_size) =
261        generate_packet_size_getter(scope, schema, decl.fields(), is_packet);
262    let conforms = if constant_width == 0 {
263        quote! { true }
264    } else {
265        let constant_width = syn::Index::from(constant_width / 8);
266        quote! { #span.len() >= #constant_width }
267    };
268
269    let visibility = if is_packet { quote!() } else { quote!(pub) };
270    let has_payload = decl.payload().is_some();
271    let has_children = scope.iter_children(decl).next().is_some();
272
273    let struct_name = if is_packet { format_ident!("{id}Data") } else { id.to_ident() };
274    let backed_fields = decl
275        .fields()
276        .filter(|f| f.id().is_some() && !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
277        .collect::<Vec<_>>();
278
279    let mut field_names =
280        backed_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
281    let mut field_types = backed_fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>();
282
283    if has_children || has_payload {
284        if is_packet {
285            field_names.push(format_ident!("child"));
286            let field_type = format_ident!("{id}DataChild");
287            field_types.push(quote!(#field_type));
288        } else {
289            field_names.push(format_ident!("payload"));
290            field_types.push(quote!(Vec<u8>));
291        }
292    }
293
294    let data_struct_decl = quote! {
295        #[derive(Debug, Clone, PartialEq, Eq)]
296        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
297        pub struct #struct_name {
298            #(#visibility #field_names: #field_types,)*
299        }
300    };
301
302    let data_struct_impl = quote! {
303        impl #struct_name {
304            fn conforms(#span: &[u8]) -> bool {
305                #conforms
306            }
307
308            #visibility fn parse(
309                #span: &[u8] #(, #parse_arg_names: #parse_arg_types)*
310            ) -> Result<Self, DecodeError> {
311                let mut cell = Cell::new(#span);
312                let packet = Self::parse_inner(&mut cell #(, #parse_arg_names)*)?;
313                // TODO(mgeisler): communicate back to user if !cell.get().is_empty()?
314                Ok(packet)
315            }
316
317            fn parse_inner(
318                mut #span: &mut Cell<&[u8]> #(, #parse_arg_names: #parse_arg_types)*
319            ) -> Result<Self, DecodeError> {
320                #field_parser
321                Ok(Self {
322                    #(#field_names,)*
323                })
324            }
325
326            fn write_to<T: BufMut>(&self, buffer: &mut T) -> Result<(), EncodeError> {
327                #field_serializer
328                Ok(())
329            }
330
331            fn get_total_size(&self) -> usize {
332                self.get_size()
333            }
334
335            fn get_size(&self) -> usize {
336                #packet_size
337            }
338        }
339    };
340
341    (data_struct_decl, data_struct_impl)
342}
343
344/// Turn the constraint into a value (such as `10` or
345/// `SomeEnum::Foo`).
346pub fn constraint_to_value(
347    all_fields: &HashMap<String, &'_ ast::Field>,
348    constraint: &ast::Constraint,
349) -> proc_macro2::TokenStream {
350    match constraint {
351        ast::Constraint { value: Some(value), .. } => {
352            let value = proc_macro2::Literal::usize_unsuffixed(*value);
353            quote!(#value)
354        }
355        // TODO(mgeisler): include type_id in `ast::Constraint` and
356        // drop the packet_scope argument.
357        ast::Constraint { tag_id: Some(tag_id), .. } => {
358            let type_id = match &all_fields[&constraint.id].desc {
359                ast::FieldDesc::Typedef { type_id, .. } => type_id.to_ident(),
360                _ => unreachable!("Invalid constraint: {constraint:?}"),
361            };
362            let tag_id = format_ident!("{}", tag_id.to_upper_camel_case());
363            quote!(#type_id::#tag_id)
364        }
365        _ => unreachable!("Invalid constraint: {constraint:?}"),
366    }
367}
368
369/// Generate code for a `ast::Decl::Packet`.
370fn generate_packet_decl(
371    scope: &analyzer::Scope<'_>,
372    schema: &analyzer::Schema,
373    endianness: ast::EndiannessValue,
374    id: &str,
375) -> proc_macro2::TokenStream {
376    let decl = scope.typedef[id];
377    let top_level = top_level_packet(scope, id);
378    let top_level_id = top_level.id().unwrap();
379    let top_level_packet = top_level_id.to_ident();
380    let top_level_data = format_ident!("{top_level_id}Data");
381    let top_level_id_lower = top_level_id.to_lowercase().to_ident();
382
383    // TODO(mgeisler): use the convert_case crate to convert between
384    // `FooBar` and `foo_bar` in the code below.
385    let span = format_ident!("bytes");
386    let id_lower = id.to_lowercase().to_ident();
387    let id_packet = id.to_ident();
388    let id_child = format_ident!("{id}Child");
389    let id_data_child = format_ident!("{id}DataChild");
390    let id_builder = format_ident!("{id}Builder");
391
392    let mut parents = scope.iter_parents_and_self(decl).collect::<Vec<_>>();
393    parents.reverse();
394
395    let parent_ids = parents.iter().map(|p| p.id().unwrap()).collect::<Vec<_>>();
396    let parent_shifted_ids = parent_ids.iter().skip(1).map(|id| id.to_ident());
397    let parent_lower_ids =
398        parent_ids.iter().map(|id| id.to_lowercase().to_ident()).collect::<Vec<_>>();
399    let parent_shifted_lower_ids = parent_lower_ids.iter().skip(1).collect::<Vec<_>>();
400    let parent_packet = parent_ids.iter().map(|id| id.to_ident());
401    let parent_data = parent_ids.iter().map(|id| format_ident!("{id}Data"));
402    let parent_data_child = parent_ids.iter().map(|id| format_ident!("{id}DataChild"));
403
404    let all_fields = {
405        let mut fields = scope
406            .iter_fields(decl)
407            .filter(|f| f.id().is_some() && !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
408            .collect::<Vec<_>>();
409        fields.sort_by_key(|f| f.id());
410        fields
411    };
412    let all_named_fields =
413        HashMap::from_iter(all_fields.iter().map(|f| (f.id().unwrap().to_string(), *f)));
414
415    let all_field_names = all_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
416    let all_field_types = all_fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>();
417    let all_field_borrows =
418        all_fields.iter().map(|f| types::rust_borrow(f, scope)).collect::<Vec<_>>();
419    let all_field_getter_names =
420        all_fields.iter().map(|f| format_ident!("get_{}", f.id().unwrap()));
421    let all_field_self_field = all_fields.iter().map(|f| {
422        for (parent, parent_id) in parents.iter().zip(parent_lower_ids.iter()) {
423            if parent.fields().any(|ff| ff.id() == f.id()) {
424                return quote!(self.#parent_id);
425            }
426        }
427        unreachable!("Could not find {f:?} in parent chain");
428    });
429
430    let all_constraints = HashMap::<String, _>::from_iter(
431        scope.iter_constraints(decl).map(|c| (c.id.to_string(), c)),
432    );
433
434    let unconstrained_fields = all_fields
435        .iter()
436        .filter(|f| !all_constraints.contains_key(f.id().unwrap()))
437        .collect::<Vec<_>>();
438    let unconstrained_field_names =
439        unconstrained_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
440    let unconstrained_field_types = unconstrained_fields.iter().map(|f| types::rust_type(f));
441
442    let rev_parents = parents.iter().rev().collect::<Vec<_>>();
443    let builder_assignments = rev_parents.iter().enumerate().map(|(idx, parent)| {
444        let parent_id = parent.id().unwrap();
445        let parent_id_lower = parent_id.to_lowercase().to_ident();
446        let parent_data = format_ident!("{parent_id}Data");
447        let parent_data_child = format_ident!("{parent_id}DataChild");
448
449        let named_fields = {
450            let mut names = parent
451                .fields()
452                .filter(|f| !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
453                .filter_map(ast::Field::id)
454                .collect::<Vec<_>>();
455            names.sort_unstable();
456            names
457        };
458
459        let mut field = named_fields.iter().map(|id| id.to_ident()).collect::<Vec<_>>();
460        let mut value = named_fields
461            .iter()
462            .map(|&id| match all_constraints.get(id) {
463                Some(constraint) => constraint_to_value(&all_named_fields, constraint),
464                None => {
465                    let id = id.to_ident();
466                    quote!(self.#id)
467                }
468            })
469            .collect::<Vec<_>>();
470
471        if parent.payload().is_some() {
472            field.push(format_ident!("child"));
473            if idx == 0 {
474                // Top-most parent, the child is simply created from
475                // our payload.
476                value.push(quote! {
477                    match self.payload {
478                        None => #parent_data_child::None,
479                        Some(bytes) => #parent_data_child::Payload(bytes),
480                    }
481                });
482            } else {
483                // Child is created from the previous parent.
484                let prev_parent_id = rev_parents[idx - 1].id().unwrap();
485                let prev_parent_id_lower = prev_parent_id.to_lowercase().to_ident();
486                let prev_parent_id = prev_parent_id.to_ident();
487                value.push(quote! {
488                    #parent_data_child::#prev_parent_id(#prev_parent_id_lower)
489                });
490            }
491        } else if scope.iter_children(parent).next().is_some() {
492            field.push(format_ident!("child"));
493            value.push(quote! { #parent_data_child::None });
494        }
495
496        quote! {
497            let #parent_id_lower = #parent_data {
498                #(#field: #value,)*
499            };
500        }
501    });
502
503    let children = scope.iter_children(decl).collect::<Vec<_>>();
504    let has_payload = decl.payload().is_some();
505    let has_children_or_payload = !children.is_empty() || has_payload;
506    let child = children.iter().map(|child| child.id().unwrap().to_ident()).collect::<Vec<_>>();
507    let child_data = child.iter().map(|child| format_ident!("{child}Data")).collect::<Vec<_>>();
508    let get_payload = (children.is_empty() && has_payload).then(|| {
509        quote! {
510            pub fn get_payload(&self) -> &[u8] {
511                match &self.#id_lower.child {
512                    #id_data_child::Payload(bytes) => &bytes,
513                    #id_data_child::None => &[],
514                }
515            }
516        }
517    });
518    let child_declaration = has_children_or_payload.then(|| {
519        quote! {
520            #[derive(Debug, Clone, PartialEq, Eq)]
521            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
522            pub enum #id_data_child {
523                #(#child(#child_data),)*
524                Payload(Bytes),
525                None,
526            }
527
528            impl #id_data_child {
529                fn get_total_size(&self) -> usize {
530                    match self {
531                        #(#id_data_child::#child(value) => value.get_total_size(),)*
532                        #id_data_child::Payload(bytes) => bytes.len(),
533                        #id_data_child::None => 0,
534                    }
535                }
536            }
537
538            #[derive(Debug, Clone, PartialEq, Eq)]
539            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
540            pub enum #id_child {
541                #(#child(#child),)*
542                Payload(Bytes),
543                None,
544            }
545        }
546    });
547    let specialize = has_children_or_payload.then(|| {
548        quote! {
549            pub fn specialize(&self) -> #id_child {
550                match &self.#id_lower.child {
551                    #(
552                        #id_data_child::#child(_) =>
553                        #id_child::#child(#child::new(self.#top_level_id_lower.clone()).unwrap()),
554                    )*
555                    #id_data_child::Payload(payload) => #id_child::Payload(payload.clone()),
556                    #id_data_child::None => #id_child::None,
557                }
558            }
559        }
560    });
561
562    let builder_payload_field = has_children_or_payload.then(|| {
563        quote! {
564            pub payload: Option<Bytes>
565        }
566    });
567
568    let ancestor_packets = parent_ids[..parent_ids.len() - 1].iter().map(|id| id.to_ident());
569    let impl_from_and_try_from = (top_level_id != id).then(|| {
570        quote! {
571            #(
572                impl From<#id_packet> for #ancestor_packets {
573                    fn from(packet: #id_packet) -> #ancestor_packets {
574                        #ancestor_packets::new(packet.#top_level_id_lower).unwrap()
575                    }
576                }
577            )*
578
579            impl TryFrom<#top_level_packet> for #id_packet {
580                type Error = DecodeError;
581                fn try_from(packet: #top_level_packet) -> Result<#id_packet, Self::Error> {
582                    #id_packet::new(packet.#top_level_id_lower)
583                }
584            }
585        }
586    });
587
588    let (data_struct_decl, data_struct_impl) = generate_data_struct(scope, schema, endianness, id);
589
590    quote! {
591        #child_declaration
592
593        #data_struct_decl
594
595        #[derive(Debug, Clone, PartialEq, Eq)]
596        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
597        pub struct #id_packet {
598            #(
599                #[cfg_attr(feature = "serde", serde(flatten))]
600                #parent_lower_ids: #parent_data,
601            )*
602        }
603
604        #[derive(Debug)]
605        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
606        pub struct #id_builder {
607            #(pub #unconstrained_field_names: #unconstrained_field_types,)*
608            #builder_payload_field
609        }
610
611        #data_struct_impl
612
613        impl Packet for #id_packet {
614            fn encoded_len(&self) -> usize {
615                self.get_size()
616            }
617            fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
618                self.#top_level_id_lower.write_to(buf)
619            }
620            fn decode(_: &[u8]) -> Result<(Self, &[u8]), DecodeError> {
621                unimplemented!("Rust legacy does not implement full packet trait")
622            }
623        }
624
625        impl TryFrom<#id_packet> for Bytes {
626            type Error = EncodeError;
627            fn try_from(packet: #id_packet) -> Result<Self, Self::Error> {
628                packet.encode_to_bytes()
629            }
630        }
631
632        impl TryFrom<#id_packet> for Vec<u8> {
633            type Error = EncodeError;
634            fn try_from(packet: #id_packet) -> Result<Self, Self::Error> {
635                packet.encode_to_vec()
636            }
637        }
638
639        #impl_from_and_try_from
640
641        impl #id_packet {
642            pub fn parse(#span: &[u8]) -> Result<Self, DecodeError> {
643                let mut cell = Cell::new(#span);
644                let packet = Self::parse_inner(&mut cell)?;
645                // TODO(mgeisler): communicate back to user if !cell.get().is_empty()?
646                Ok(packet)
647            }
648
649            fn parse_inner(mut bytes: &mut Cell<&[u8]>) -> Result<Self, DecodeError> {
650                let data = #top_level_data::parse_inner(&mut bytes)?;
651                Self::new(data)
652            }
653
654            #specialize
655
656            fn new(#top_level_id_lower: #top_level_data) -> Result<Self, DecodeError> {
657                #(
658                    let #parent_shifted_lower_ids = match &#parent_lower_ids.child {
659                        #parent_data_child::#parent_shifted_ids(value) => value.clone(),
660                        _ => return Err(DecodeError::InvalidChildError {
661                            expected: stringify!(#parent_data_child::#parent_shifted_ids),
662                            actual: format!("{:?}", &#parent_lower_ids.child),
663                        }),
664                    };
665                )*
666                Ok(Self { #(#parent_lower_ids),* })
667            }
668
669            #(pub fn #all_field_getter_names(&self) -> #all_field_borrows #all_field_types {
670                #all_field_borrows #all_field_self_field.#all_field_names
671            })*
672
673            #get_payload
674
675            fn write_to(&self, buffer: &mut impl BufMut) -> Result<(), EncodeError> {
676                self.#id_lower.write_to(buffer)
677            }
678
679            pub fn get_size(&self) -> usize {
680                self.#top_level_id_lower.get_size()
681            }
682        }
683
684        impl #id_builder {
685            pub fn build(self) -> #id_packet {
686                #(#builder_assignments;)*
687                #id_packet::new(#top_level_id_lower).unwrap()
688            }
689        }
690
691        #(
692            impl From<#id_builder> for #parent_packet {
693                fn from(builder: #id_builder) -> #parent_packet {
694                    builder.build().into()
695                }
696            }
697        )*
698    }
699}
700
701/// Generate code for a `ast::Decl::Struct`.
702fn generate_struct_decl(
703    scope: &analyzer::Scope<'_>,
704    schema: &analyzer::Schema,
705    endianness: ast::EndiannessValue,
706    id: &str,
707) -> proc_macro2::TokenStream {
708    let (struct_decl, struct_impl) = generate_data_struct(scope, schema, endianness, id);
709    quote! {
710        #struct_decl
711        #struct_impl
712    }
713}
714
715/// Generate an enum declaration.
716///
717/// # Arguments
718/// * `id` - Enum identifier.
719/// * `tags` - List of enum tags.
720/// * `width` - Width of the backing type of the enum, in bits.
721/// * `open` - Whether to generate an open or closed enum. Open enums have
722///            an additional Unknown case for unmatched valued. Complete
723///            enums (where the full range of values is covered) are
724///            automatically closed.
725fn generate_enum_decl(id: &str, tags: &[ast::Tag], width: usize) -> proc_macro2::TokenStream {
726    // Determine if the enum is open, i.e. a default tag is defined.
727    fn enum_default_tag(tags: &[ast::Tag]) -> Option<ast::TagOther> {
728        tags.iter()
729            .filter_map(|tag| match tag {
730                ast::Tag::Other(tag) => Some(tag.clone()),
731                _ => None,
732            })
733            .next()
734    }
735
736    // Determine if the enum is complete, i.e. all values in the backing
737    // integer range have a matching tag in the original declaration.
738    fn enum_is_complete(tags: &[ast::Tag], max: usize) -> bool {
739        let mut ranges = tags
740            .iter()
741            .filter_map(|tag| match tag {
742                ast::Tag::Value(tag) => Some((tag.value, tag.value)),
743                ast::Tag::Range(tag) => Some(tag.range.clone().into_inner()),
744                _ => None,
745            })
746            .collect::<Vec<_>>();
747        ranges.sort_unstable();
748        ranges.first().unwrap().0 == 0
749            && ranges.last().unwrap().1 == max
750            && ranges.windows(2).all(|window| {
751                if let [left, right] = window {
752                    left.1 == right.0 - 1
753                } else {
754                    false
755                }
756            })
757    }
758
759    // Determine if the enum is primitive, i.e. does not contain any tag range.
760    fn enum_is_primitive(tags: &[ast::Tag]) -> bool {
761        tags.iter().all(|tag| matches!(tag, ast::Tag::Value(_)))
762    }
763
764    // Return the maximum value for the scalar type.
765    fn scalar_max(width: usize) -> usize {
766        if width >= usize::BITS as usize {
767            usize::MAX
768        } else {
769            (1 << width) - 1
770        }
771    }
772
773    // Format an enum tag identifier to rust upper caml case.
774    fn format_tag_ident(id: &str) -> proc_macro2::TokenStream {
775        let id = format_ident!("{}", id.to_upper_camel_case());
776        quote! { #id }
777    }
778
779    // Format a constant value as hexadecimal constant.
780    fn format_value(value: usize) -> LitInt {
781        syn::parse_str::<syn::LitInt>(&format!("{:#x}", value)).unwrap()
782    }
783
784    // Backing type for the enum.
785    let backing_type = types::Integer::new(width);
786    let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width));
787    let range_max = scalar_max(width);
788    let default_tag = enum_default_tag(tags);
789    let is_open = default_tag.is_some();
790    let is_complete = enum_is_complete(tags, scalar_max(width));
791    let is_primitive = enum_is_primitive(tags);
792    let name = id.to_ident();
793
794    // Generate the variant cases for the enum declaration.
795    // Tags declared in ranges are flattened in the same declaration.
796    let use_variant_values = is_primitive && (is_complete || !is_open);
797    let repr_u64 = use_variant_values.then(|| quote! { #[repr(u64)] });
798    let mut variants = vec![];
799    for tag in tags.iter() {
800        match tag {
801            ast::Tag::Value(tag) if use_variant_values => {
802                let id = format_tag_ident(&tag.id);
803                let value = format_value(tag.value);
804                variants.push(quote! { #id = #value })
805            }
806            ast::Tag::Value(tag) => variants.push(format_tag_ident(&tag.id)),
807            ast::Tag::Range(tag) => {
808                variants.extend(tag.tags.iter().map(|tag| format_tag_ident(&tag.id)));
809                let id = format_tag_ident(&tag.id);
810                variants.push(quote! { #id(Private<#backing_type>) })
811            }
812            ast::Tag::Other(_) => (),
813        }
814    }
815
816    // Generate the cases for parsing the enum value from an integer.
817    let mut from_cases = vec![];
818    for tag in tags.iter() {
819        match tag {
820            ast::Tag::Value(tag) => {
821                let id = format_tag_ident(&tag.id);
822                let value = format_value(tag.value);
823                from_cases.push(quote! { #value => Ok(#name::#id) })
824            }
825            ast::Tag::Range(tag) => {
826                from_cases.extend(tag.tags.iter().map(|tag| {
827                    let id = format_tag_ident(&tag.id);
828                    let value = format_value(tag.value);
829                    quote! { #value => Ok(#name::#id) }
830                }));
831                let id = format_tag_ident(&tag.id);
832                let start = format_value(*tag.range.start());
833                let end = format_value(*tag.range.end());
834                from_cases.push(quote! { #start ..= #end => Ok(#name::#id(Private(value))) })
835            }
836            ast::Tag::Other(_) => (),
837        }
838    }
839
840    // Generate the cases for serializing the enum value to an integer.
841    let mut into_cases = vec![];
842    for tag in tags.iter() {
843        match tag {
844            ast::Tag::Value(tag) => {
845                let id = format_tag_ident(&tag.id);
846                let value = format_value(tag.value);
847                into_cases.push(quote! { #name::#id => #value })
848            }
849            ast::Tag::Range(tag) => {
850                into_cases.extend(tag.tags.iter().map(|tag| {
851                    let id = format_tag_ident(&tag.id);
852                    let value = format_value(tag.value);
853                    quote! { #name::#id => #value }
854                }));
855                let id = format_tag_ident(&tag.id);
856                into_cases.push(quote! { #name::#id(Private(value)) => *value })
857            }
858            ast::Tag::Other(_) => (),
859        }
860    }
861
862    // Generate a default case if the enum is open and incomplete.
863    if !is_complete && is_open {
864        let unknown_id = format_tag_ident(&default_tag.unwrap().id);
865        let range_max = format_value(range_max);
866        variants.push(quote! { #unknown_id(Private<#backing_type>) });
867        from_cases.push(quote! { 0..=#range_max => Ok(#name::#unknown_id(Private(value))) });
868        into_cases.push(quote! { #name::#unknown_id(Private(value)) => *value });
869    }
870
871    // Generate an error case if the enum size is lower than the backing
872    // type size, or if the enum is closed or incomplete.
873    if backing_type.width != width || (!is_complete && !is_open) {
874        from_cases.push(quote! { _ => Err(value) });
875    }
876
877    // Derive other Into<uN> and Into<iN> implementations from the explicit
878    // implementation, where the type is larger than the backing type.
879    let derived_signed_into_types = [8, 16, 32, 64]
880        .into_iter()
881        .filter(|w| *w > width)
882        .map(|w| syn::parse_str::<syn::Type>(&format!("i{}", w)).unwrap());
883    let derived_unsigned_into_types = [8, 16, 32, 64]
884        .into_iter()
885        .filter(|w| *w >= width && *w != backing_type.width)
886        .map(|w| syn::parse_str::<syn::Type>(&format!("u{}", w)).unwrap());
887    let derived_into_types = derived_signed_into_types.chain(derived_unsigned_into_types);
888
889    quote! {
890        #repr_u64
891        #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
892        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
893        #[cfg_attr(feature = "serde", serde(try_from = #backing_type_str, into = #backing_type_str))]
894        pub enum #name {
895            #(#variants,)*
896        }
897
898        impl TryFrom<#backing_type> for #name {
899            type Error = #backing_type;
900            fn try_from(value: #backing_type) -> Result<Self, Self::Error> {
901                match value {
902                    #(#from_cases,)*
903                }
904            }
905        }
906
907        impl From<&#name> for #backing_type {
908            fn from(value: &#name) -> Self {
909                match value {
910                    #(#into_cases,)*
911                }
912            }
913        }
914
915        impl From<#name> for #backing_type {
916            fn from(value: #name) -> Self {
917                (&value).into()
918            }
919        }
920
921        #(impl From<#name> for #derived_into_types {
922            fn from(value: #name) -> Self {
923                #backing_type::from(value) as Self
924            }
925        })*
926    }
927}
928
929/// Generate the declaration for a custom field of static size.
930///
931/// * `id` - Enum identifier.
932/// * `width` - Width of the backing type of the enum, in bits.
933fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStream {
934    let id = id.to_ident();
935    let backing_type = types::Integer::new(width);
936    let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width));
937    let max_value = mask_bits(width, &format!("u{}", backing_type.width));
938    let common = quote! {
939        impl From<&#id> for #backing_type {
940            fn from(value: &#id) -> #backing_type {
941                value.0
942            }
943        }
944
945        impl From<#id> for #backing_type {
946            fn from(value: #id) -> #backing_type {
947                value.0
948            }
949        }
950    };
951
952    if backing_type.width == width {
953        quote! {
954            #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
955            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
956            #[cfg_attr(feature = "serde", serde(from = #backing_type_str, into = #backing_type_str))]
957            pub struct #id(#backing_type);
958
959            #common
960
961            impl From<#backing_type> for #id {
962                fn from(value: #backing_type) -> Self {
963                    #id(value)
964                }
965            }
966        }
967    } else {
968        quote! {
969            #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
970            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
971            #[cfg_attr(feature = "serde", serde(try_from = #backing_type_str, into = #backing_type_str))]
972            pub struct #id(#backing_type);
973
974            #common
975
976            impl TryFrom<#backing_type> for #id {
977                type Error = #backing_type;
978                fn try_from(value: #backing_type) -> Result<Self, Self::Error> {
979                    if value > #max_value {
980                        Err(value)
981                    } else {
982                        Ok(#id(value))
983                    }
984                }
985            }
986        }
987    }
988}
989
990fn generate_decl(
991    scope: &analyzer::Scope<'_>,
992    schema: &analyzer::Schema,
993    file: &ast::File,
994    decl: &ast::Decl,
995) -> proc_macro2::TokenStream {
996    match &decl.desc {
997        ast::DeclDesc::Packet { id, .. } => {
998            generate_packet_decl(scope, schema, file.endianness.value, id)
999        }
1000        ast::DeclDesc::Struct { id, parent_id: None, .. } => {
1001            // TODO(mgeisler): handle structs with parents. We could
1002            // generate code for them, but the code is not useful
1003            // since it would require the caller to unpack everything
1004            // manually. We either need to change the API, or
1005            // implement the recursive (de)serialization.
1006            generate_struct_decl(scope, schema, file.endianness.value, id)
1007        }
1008        ast::DeclDesc::Enum { id, tags, width } => generate_enum_decl(id, tags, *width),
1009        ast::DeclDesc::CustomField { id, width: Some(width), .. } => {
1010            generate_custom_field_decl(id, *width)
1011        }
1012        _ => todo!("unsupported Decl::{:?}", decl),
1013    }
1014}
1015
1016/// Generate Rust code from an AST.
1017///
1018/// The code is not formatted, pipe it through `rustfmt` to get
1019/// readable source code.
1020pub fn generate_tokens(
1021    sources: &ast::SourceDatabase,
1022    file: &ast::File,
1023) -> proc_macro2::TokenStream {
1024    let source = sources.get(file.file).expect("could not read source");
1025    let preamble = preamble::generate(Path::new(source.name()));
1026
1027    let scope = analyzer::Scope::new(file).expect("could not create scope");
1028    let schema = analyzer::Schema::new(file);
1029    let decls = file.declarations.iter().map(|decl| generate_decl(&scope, &schema, file, decl));
1030    quote! {
1031        #preamble
1032
1033        #(#decls)*
1034    }
1035}
1036
1037/// Generate formatted Rust code from an AST.
1038///
1039/// The code is not formatted, pipe it through `rustfmt` to get
1040/// readable source code.
1041pub fn generate(sources: &ast::SourceDatabase, file: &ast::File) -> String {
1042    let syntax_tree = syn::parse2(generate_tokens(sources, file)).expect("Could not parse code");
1043    prettyplease::unparse(&syntax_tree)
1044}
1045
1046#[cfg(test)]
1047mod tests {
1048    use super::*;
1049    use crate::analyzer;
1050    use crate::ast;
1051    use crate::parser::parse_inline;
1052    use crate::test_utils::{assert_snapshot_eq, format_rust};
1053    use googletest::prelude::{elements_are, eq, expect_that};
1054    use paste::paste;
1055
1056    /// Parse a string fragment as a PDL file.
1057    ///
1058    /// # Panics
1059    ///
1060    /// Panics on parse errors.
1061    pub fn parse_str(text: &str) -> ast::File {
1062        let mut db = ast::SourceDatabase::new();
1063        let file = parse_inline(&mut db, "stdin", String::from(text)).expect("parse error");
1064        analyzer::analyze(&file).expect("analyzer error")
1065    }
1066
1067    #[googletest::test]
1068    fn test_find_constrained_parent_fields() -> googletest::Result<()> {
1069        let code = "
1070              little_endian_packets
1071              packet Parent {
1072                a: 8,
1073                b: 8,
1074                c: 8,
1075                _payload_,
1076              }
1077              packet Child: Parent(a = 10) {
1078                x: 8,
1079                _payload_,
1080              }
1081              packet GrandChild: Child(b = 20) {
1082                y: 8,
1083                _payload_,
1084              }
1085              packet GrandGrandChild: GrandChild(c = 30) {
1086                z: 8,
1087              }
1088            ";
1089        let file = parse_str(code);
1090        let scope = analyzer::Scope::new(&file).unwrap();
1091        let find_fields = |id| {
1092            find_constrained_parent_fields(&scope, id)
1093                .iter()
1094                .map(|field| field.id().unwrap())
1095                .collect::<Vec<_>>()
1096        };
1097
1098        expect_that!(find_fields("Parent"), elements_are![]);
1099        expect_that!(find_fields("Child"), elements_are![eq("b"), eq("c")]);
1100        expect_that!(find_fields("GrandChild"), elements_are![eq("c")]);
1101        expect_that!(find_fields("GrandGrandChild"), elements_are![]);
1102        Ok(())
1103    }
1104
1105    /// Create a unit test for the given PDL `code`.
1106    ///
1107    /// The unit test will compare the generated Rust code for all
1108    /// declarations with previously saved snapshots. The snapshots
1109    /// are read from `"tests/generated/{name}_{endianness}_{id}.rs"`
1110    /// where `is` taken from the declaration.
1111    ///
1112    /// When adding new tests or modifying existing ones, use
1113    /// `UPDATE_SNAPSHOTS=1 cargo test` to automatically populate the
1114    /// snapshots with the expected output.
1115    ///
1116    /// The `code` cannot have an endianness declaration, instead you
1117    /// must supply either `little_endian` or `big_endian` as
1118    /// `endianness`.
1119    macro_rules! make_pdl_test {
1120        ($name:ident, $code:expr, $endianness:ident) => {
1121            paste! {
1122                #[test]
1123                fn [< test_ $name _ $endianness >]() {
1124                    let name = stringify!($name);
1125                    let endianness = stringify!($endianness);
1126                    let code = format!("{endianness}_packets\n{}", $code);
1127                    let mut db = ast::SourceDatabase::new();
1128                    let file = parse_inline(&mut db, "test", code).unwrap();
1129                    let file = analyzer::analyze(&file).unwrap();
1130                    let actual_code = generate(&db, &file);
1131                    assert_snapshot_eq(
1132                        &format!("tests/generated/rust_legacy/{name}_{endianness}.rs"),
1133                        &format_rust(&actual_code),
1134                    );
1135                }
1136            }
1137        };
1138    }
1139
1140    /// Create little- and bit-endian tests for the given PDL `code`.
1141    ///
1142    /// The `code` cannot have an endianness declaration: we will
1143    /// automatically generate unit tests for both
1144    /// "little_endian_packets" and "big_endian_packets".
1145    macro_rules! test_pdl {
1146        ($name:ident, $code:expr $(,)?) => {
1147            make_pdl_test!($name, $code, little_endian);
1148            make_pdl_test!($name, $code, big_endian);
1149        };
1150    }
1151
1152    test_pdl!(packet_decl_empty, "packet Foo {}");
1153
1154    test_pdl!(packet_decl_8bit_scalar, " packet Foo { x:  8 }");
1155    test_pdl!(packet_decl_24bit_scalar, "packet Foo { x: 24 }");
1156    test_pdl!(packet_decl_64bit_scalar, "packet Foo { x: 64 }");
1157
1158    test_pdl!(
1159        enum_declaration,
1160        r#"
1161        enum IncompleteTruncatedClosed : 3 {
1162            A = 0,
1163            B = 1,
1164        }
1165
1166        enum IncompleteTruncatedOpen : 3 {
1167            A = 0,
1168            B = 1,
1169            UNKNOWN = ..
1170        }
1171
1172        enum IncompleteTruncatedClosedWithRange : 3 {
1173            A = 0,
1174            B = 1..6 {
1175                X = 1,
1176                Y = 2,
1177            }
1178        }
1179
1180        enum IncompleteTruncatedOpenWithRange : 3 {
1181            A = 0,
1182            B = 1..6 {
1183                X = 1,
1184                Y = 2,
1185            },
1186            UNKNOWN = ..
1187        }
1188
1189        enum CompleteTruncated : 3 {
1190            A = 0,
1191            B = 1,
1192            C = 2,
1193            D = 3,
1194            E = 4,
1195            F = 5,
1196            G = 6,
1197            H = 7,
1198        }
1199
1200        enum CompleteTruncatedWithRange : 3 {
1201            A = 0,
1202            B = 1..7 {
1203                X = 1,
1204                Y = 2,
1205            }
1206        }
1207
1208        enum CompleteWithRange : 8 {
1209            A = 0,
1210            B = 1,
1211            C = 2..255,
1212        }
1213        "#
1214    );
1215
1216    test_pdl!(
1217        custom_field_declaration,
1218        r#"
1219        // Still unsupported.
1220        // custom_field Dynamic "dynamic"
1221
1222        // Should generate a type with From<u32> implementation.
1223        custom_field ExactSize : 32 "exact_size"
1224
1225        // Should generate a type with TryFrom<u32> implementation.
1226        custom_field TruncatedSize : 24 "truncated_size"
1227        "#
1228    );
1229
1230    test_pdl!(
1231        packet_decl_simple_scalars,
1232        r#"
1233          packet Foo {
1234            x: 8,
1235            y: 16,
1236            z: 24,
1237          }
1238        "#
1239    );
1240
1241    test_pdl!(
1242        packet_decl_complex_scalars,
1243        r#"
1244          packet Foo {
1245            a: 3,
1246            b: 8,
1247            c: 5,
1248            d: 24,
1249            e: 12,
1250            f: 4,
1251          }
1252        "#,
1253    );
1254
1255    // Test that we correctly mask a byte-sized value in the middle of
1256    // a chunk.
1257    test_pdl!(
1258        packet_decl_mask_scalar_value,
1259        r#"
1260          packet Foo {
1261            a: 2,
1262            b: 24,
1263            c: 6,
1264          }
1265        "#,
1266    );
1267
1268    test_pdl!(
1269        struct_decl_complex_scalars,
1270        r#"
1271          struct Foo {
1272            a: 3,
1273            b: 8,
1274            c: 5,
1275            d: 24,
1276            e: 12,
1277            f: 4,
1278          }
1279        "#,
1280    );
1281
1282    test_pdl!(packet_decl_8bit_enum, " enum Foo :  8 { A = 1, B = 2 } packet Bar { x: Foo }");
1283    test_pdl!(packet_decl_24bit_enum, "enum Foo : 24 { A = 1, B = 2 } packet Bar { x: Foo }");
1284    test_pdl!(packet_decl_64bit_enum, "enum Foo : 64 { A = 1, B = 2 } packet Bar { x: Foo }");
1285
1286    test_pdl!(
1287        packet_decl_mixed_scalars_enums,
1288        "
1289          enum Enum7 : 7 {
1290            A = 1,
1291            B = 2,
1292          }
1293
1294          enum Enum9 : 9 {
1295            A = 1,
1296            B = 2,
1297          }
1298
1299          packet Foo {
1300            x: Enum7,
1301            y: 5,
1302            z: Enum9,
1303            w: 3,
1304          }
1305        "
1306    );
1307
1308    test_pdl!(packet_decl_8bit_scalar_array, " packet Foo { x:  8[3] }");
1309    test_pdl!(packet_decl_24bit_scalar_array, "packet Foo { x: 24[5] }");
1310    test_pdl!(packet_decl_64bit_scalar_array, "packet Foo { x: 64[7] }");
1311
1312    test_pdl!(
1313        packet_decl_8bit_enum_array,
1314        "enum Foo :  8 { FOO_BAR = 1, BAZ = 2 } packet Bar { x: Foo[3] }"
1315    );
1316    test_pdl!(
1317        packet_decl_24bit_enum_array,
1318        "enum Foo : 24 { FOO_BAR = 1, BAZ = 2 } packet Bar { x: Foo[5] }"
1319    );
1320    test_pdl!(
1321        packet_decl_64bit_enum_array,
1322        "enum Foo : 64 { FOO_BAR = 1, BAZ = 2 } packet Bar { x: Foo[7] }"
1323    );
1324
1325    test_pdl!(
1326        packet_decl_array_dynamic_count,
1327        "
1328          packet Foo {
1329            _count_(x): 5,
1330            padding: 3,
1331            x: 24[]
1332          }
1333        "
1334    );
1335
1336    test_pdl!(
1337        packet_decl_array_dynamic_size,
1338        "
1339          packet Foo {
1340            _size_(x): 5,
1341            padding: 3,
1342            x: 24[]
1343          }
1344        "
1345    );
1346
1347    test_pdl!(
1348        packet_decl_array_unknown_element_width_dynamic_size,
1349        "
1350          struct Foo {
1351            _count_(a): 40,
1352            a: 16[],
1353          }
1354
1355          packet Bar {
1356            _size_(x): 40,
1357            x: Foo[],
1358          }
1359        "
1360    );
1361
1362    test_pdl!(
1363        packet_decl_array_unknown_element_width_dynamic_count,
1364        "
1365          struct Foo {
1366            _count_(a): 40,
1367            a: 16[],
1368          }
1369
1370          packet Bar {
1371            _count_(x): 40,
1372            x: Foo[],
1373          }
1374        "
1375    );
1376
1377    test_pdl!(
1378        packet_decl_array_with_padding,
1379        "
1380          struct Foo {
1381            _count_(a): 40,
1382            a: 16[],
1383          }
1384
1385          packet Bar {
1386            a: Foo[],
1387            _padding_ [128],
1388          }
1389        "
1390    );
1391
1392    test_pdl!(
1393        packet_decl_reserved_field,
1394        "
1395          packet Foo {
1396            _reserved_: 40,
1397          }
1398        "
1399    );
1400
1401    test_pdl!(
1402        packet_decl_custom_field,
1403        r#"
1404          custom_field Bar1 : 24 "exact"
1405          custom_field Bar2 : 32 "truncated"
1406
1407          packet Foo {
1408            a: Bar1,
1409            b: Bar2,
1410          }
1411        "#
1412    );
1413
1414    test_pdl!(
1415        packet_decl_fixed_scalar_field,
1416        "
1417          packet Foo {
1418            _fixed_ = 7 : 7,
1419            b: 57,
1420          }
1421        "
1422    );
1423
1424    test_pdl!(
1425        packet_decl_fixed_enum_field,
1426        "
1427          enum Enum7 : 7 {
1428            A = 1,
1429            B = 2,
1430          }
1431
1432          packet Foo {
1433              _fixed_ = A : Enum7,
1434              b: 57,
1435          }
1436        "
1437    );
1438
1439    test_pdl!(
1440        packet_decl_payload_field_variable_size,
1441        "
1442          packet Foo {
1443              a: 8,
1444              _size_(_payload_): 8,
1445              _payload_,
1446              b: 16,
1447          }
1448        "
1449    );
1450
1451    test_pdl!(
1452        packet_decl_payload_field_unknown_size,
1453        "
1454          packet Foo {
1455              a: 24,
1456              _payload_,
1457          }
1458        "
1459    );
1460
1461    test_pdl!(
1462        packet_decl_payload_field_unknown_size_terminal,
1463        "
1464          packet Foo {
1465              _payload_,
1466              a: 24,
1467          }
1468        "
1469    );
1470
1471    test_pdl!(
1472        packet_decl_child_packets,
1473        "
1474          enum Enum16 : 16 {
1475            A = 1,
1476            B = 2,
1477          }
1478
1479          packet Foo {
1480              a: 8,
1481              b: Enum16,
1482              _size_(_payload_): 8,
1483              _payload_
1484          }
1485
1486          packet Bar : Foo (a = 100) {
1487              x: 8,
1488          }
1489
1490          packet Baz : Foo (b = B) {
1491              y: 16,
1492          }
1493        "
1494    );
1495
1496    test_pdl!(
1497        packet_decl_grand_children,
1498        "
1499          enum Enum16 : 16 {
1500            A = 1,
1501            B = 2,
1502          }
1503
1504          packet Parent {
1505              foo: Enum16,
1506              bar: Enum16,
1507              baz: Enum16,
1508              _size_(_payload_): 8,
1509              _payload_
1510          }
1511
1512          packet Child : Parent (foo = A) {
1513              quux: Enum16,
1514              _payload_,
1515          }
1516
1517          packet GrandChild : Child (bar = A, quux = A) {
1518              _body_,
1519          }
1520
1521          packet GrandGrandChild : GrandChild (baz = A) {
1522              _body_,
1523          }
1524        "
1525    );
1526
1527    test_pdl!(
1528        packet_decl_parent_with_no_payload,
1529        "
1530          enum Enum8 : 8 {
1531            A = 0,
1532          }
1533
1534          packet Parent {
1535            v : Enum8,
1536          }
1537
1538          packet Child : Parent (v = A) {
1539          }
1540        "
1541    );
1542
1543    test_pdl!(
1544        packet_decl_parent_with_alias_child,
1545        "
1546          enum Enum8 : 8 {
1547            A = 0,
1548            B = 1,
1549            C = 2,
1550          }
1551
1552          packet Parent {
1553            v : Enum8,
1554            _payload_,
1555          }
1556
1557          packet AliasChild : Parent {
1558            _payload_
1559          }
1560
1561          packet NormalChild : Parent (v = A) {
1562          }
1563
1564          packet NormalGrandChild1 : AliasChild (v = B) {
1565          }
1566
1567          packet NormalGrandChild2 : AliasChild (v = C) {
1568              _payload_
1569          }
1570        "
1571    );
1572
1573    test_pdl!(
1574        reserved_identifier,
1575        "
1576          packet Test {
1577            type: 8,
1578          }
1579        "
1580    );
1581
1582    test_pdl!(
1583        payload_with_size_modifier,
1584        "
1585        packet Test {
1586            _size_(_payload_): 8,
1587            _payload_ : [+1],
1588        }
1589        "
1590    );
1591
1592    // TODO(mgeisler): enable this test when we have an approach to
1593    // struct fields with parents.
1594    //
1595    // test_pdl!(
1596    //     struct_decl_child_structs,
1597    //     "
1598    //       enum Enum16 : 16 {
1599    //         A = 1,
1600    //         B = 2,
1601    //       }
1602    //
1603    //       struct Foo {
1604    //           a: 8,
1605    //           b: Enum16,
1606    //           _size_(_payload_): 8,
1607    //           _payload_
1608    //       }
1609    //
1610    //       struct Bar : Foo (a = 100) {
1611    //           x: 8,
1612    //       }
1613    //
1614    //       struct Baz : Foo (b = B) {
1615    //           y: 16,
1616    //       }
1617    //     "
1618    // );
1619    //
1620    // test_pdl!(
1621    //     struct_decl_grand_children,
1622    //     "
1623    //       enum Enum16 : 16 {
1624    //         A = 1,
1625    //         B = 2,
1626    //       }
1627    //
1628    //       struct Parent {
1629    //           foo: Enum16,
1630    //           bar: Enum16,
1631    //           baz: Enum16,
1632    //           _size_(_payload_): 8,
1633    //           _payload_
1634    //       }
1635    //
1636    //       struct Child : Parent (foo = A) {
1637    //           quux: Enum16,
1638    //           _payload_,
1639    //       }
1640    //
1641    //       struct GrandChild : Child (bar = A, quux = A) {
1642    //           _body_,
1643    //       }
1644    //
1645    //       struct GrandGrandChild : GrandChild (baz = A) {
1646    //           _body_,
1647    //       }
1648    //     "
1649    // );
1650}