Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67485f7e authored by Martin Geisler's avatar Martin Geisler Committed by Cherrypicker Worker
Browse files

pdl: Extract generate_data_struct helper

This helper will be used to generate the common code for both packets
and structs.

Tag: #feature
Bug: 228306436
Test: atest pdl_tests pdl_rust_generator_tests_{le,be}
(cherry picked from https://android-review.googlesource.com/q/commit:e441c3308a95385c9c023b781fe457243b759ecb)
Merged-In: Ib193838a5d7b0786ede72ee53ebe580b6d847b8f
Change-Id: Ib193838a5d7b0786ede72ee53ebe580b6d847b8f
parent debf2f1d
Loading
Loading
Loading
Loading
+90 −61
Original line number Diff line number Diff line
@@ -170,6 +170,93 @@ fn find_constrained_parent_fields<'a>(
    })
}

/// Generate the declaration and implementation for a data struct.
///
/// This struct will hold the data for a packet or a struct. It knows
/// how to parse and serialize its own fields.
fn generate_data_struct(
    scope: &lint::Scope<'_>,
    endianness: ast::EndiannessValue,
    id: &str,
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
    let packet_scope = &scope.scopes[&scope.typedef[id]];
    let id_data = format_ident!("{id}Data");

    let fields_with_ids =
        packet_scope.fields.iter().filter(|f| f.id().is_some()).collect::<Vec<_>>();
    let field_names =
        fields_with_ids.iter().map(|f| format_ident!("{}", f.id().unwrap())).collect::<Vec<_>>();

    let span = format_ident!("bytes");
    let serializer_span = format_ident!("buffer");
    let mut field_declarations = FieldDeclarations::new(scope, id);
    let mut field_parser = FieldParser::new(scope, endianness, id, &span);
    let mut field_serializer = FieldSerializer::new(scope, endianness, id, &serializer_span);
    for field in &packet_scope.fields {
        field_declarations.add(field);
        field_parser.add(field);
        field_serializer.add(field);
    }
    field_declarations.done();
    field_parser.done();

    let parse_fields = find_constrained_parent_fields(scope, id).collect::<Vec<_>>();
    let parse_field_names =
        parse_fields.iter().map(|f| format_ident!("{}", f.id().unwrap())).collect::<Vec<_>>();
    let parse_field_types = parse_fields.iter().map(|f| types::rust_type(f));

    let (constant_width, packet_size) = generate_packet_size_getter(scope, &packet_scope.fields);
    let conforms = if constant_width == 0 {
        quote! { true }
    } else {
        let constant_width = syn::Index::from(constant_width / 8);
        quote! { #span.len() >= #constant_width }
    };

    let has_payload = packet_scope.payload.is_some();
    let children = get_packet_children(scope, id);
    let has_children_or_payload = !children.is_empty() || has_payload;
    let child_field = has_children_or_payload.then(|| quote!(child));

    let data_struct_decl = quote! {
        #[derive(Debug)]
        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
        pub struct #id_data {
            #field_declarations
        }
    };

    let data_struct_impl = quote! {
        impl #id_data {
            fn conforms(#span: &[u8]) -> bool {
                #conforms
            }

            fn parse(mut #span: &mut Cell<&[u8]> #(, #parse_field_names: #parse_field_types)*) -> Result<Self> {
                #field_parser
                Ok(Self {
                    #(#field_names,)*
                    #child_field
                })
            }

            fn write_to(&self, buffer: &mut BytesMut) {
                #field_serializer
            }

            fn get_total_size(&self) -> usize {
                self.get_size()
            }

            fn get_size(&self) -> usize {
                #packet_size
            }
        }
    };

    (data_struct_decl, data_struct_impl)
}

/// Find all parents from `id`.
///
/// This includes the `Decl` for `id` itself.
@@ -229,30 +316,12 @@ fn generate_packet_decl(
    // TODO(mgeisler): use the convert_case crate to convert between
    // `FooBar` and `foo_bar` in the code below.
    let span = format_ident!("bytes");
    let serializer_span = format_ident!("buffer");
    let mut field_declarations = FieldDeclarations::new(scope, id);
    let mut field_parser = FieldParser::new(scope, endianness, id, &span);
    let mut field_serializer = FieldSerializer::new(scope, endianness, id, &serializer_span);
    for field in &packet_scope.fields {
        field_declarations.add(field);
        field_parser.add(field);
        field_serializer.add(field);
    }
    field_declarations.done();
    field_parser.done();

    let id_lower = format_ident!("{}", id.to_lowercase());
    let id_packet = format_ident!("{id}");
    let id_child = format_ident!("{id}Child");
    let id_data = format_ident!("{id}Data");
    let id_data_child = format_ident!("{id}DataChild");
    let id_builder = format_ident!("{id}Builder");

    let fields_with_ids =
        packet_scope.fields.iter().filter(|f| f.id().is_some()).collect::<Vec<_>>();
    let field_names =
        fields_with_ids.iter().map(|f| format_ident!("{}", f.id().unwrap())).collect::<Vec<_>>();

    let parents = find_parents(scope, id);
    let parent_ids = parents.iter().map(|p| p.id().unwrap()).collect::<Vec<_>>();
    let parent_shifted_ids = parent_ids.iter().skip(1).map(|id| format_ident!("{id}"));
@@ -283,11 +352,6 @@ fn generate_packet_decl(
        unreachable!("Could not find {f:?} in parent chain");
    });

    let parse_fields = find_constrained_parent_fields(scope, id).collect::<Vec<_>>();
    let parse_field_names =
        parse_fields.iter().map(|f| format_ident!("{}", f.id().unwrap())).collect::<Vec<_>>();
    let parse_field_types = parse_fields.iter().map(|f| types::rust_type(f));

    let unconstrained_fields = all_fields
        .iter()
        .filter(|f| !packet_scope.all_constraints.contains_key(f.id().unwrap()))
@@ -398,7 +462,6 @@ fn generate_packet_decl(
            }
        }
    });
    let child_field = has_children_or_payload.then(|| quote!(child));
    let builder_payload_field = has_children_or_payload.then(|| {
        quote! {
            pub payload: Option<Bytes>
@@ -426,22 +489,12 @@ fn generate_packet_decl(
        }
    });

    let (constant_width, packet_size) = generate_packet_size_getter(scope, &packet_scope.fields);
    let conforms = if constant_width == 0 {
        quote! { true }
    } else {
        let constant_width = syn::Index::from(constant_width / 8);
        quote! { #span.len() >= #constant_width }
    };
    let (data_struct_decl, data_struct_impl) = generate_data_struct(scope, endianness, id);

    quote! {
        #child_declaration

        #[derive(Debug)]
        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
        pub struct #id_data {
            #field_declarations
        }
        #data_struct_decl

        #[derive(Debug, Clone)]
        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -459,31 +512,7 @@ fn generate_packet_decl(
            #builder_payload_field
        }

        impl #id_data {
            fn conforms(#span: &[u8]) -> bool {
                #conforms
            }

            fn parse(mut #span: &mut Cell<&[u8]> #(, #parse_field_names: #parse_field_types)*) -> Result<Self> {
                #field_parser
                Ok(Self {
                    #(#field_names,)*
                    #child_field
                })
            }

            fn write_to(&self, buffer: &mut BytesMut) {
                #field_serializer
            }

            fn get_total_size(&self) -> usize {
                self.get_size()
            }

            fn get_size(&self) -> usize {
                #packet_size
            }
        }
        #data_struct_impl

        impl Packet for #id_packet {
            fn to_bytes(self) -> Bytes {