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

Commit 11a7eeb0 authored by Henri Chataing's avatar Henri Chataing
Browse files

pdl: Implement custom fields for rust generator

Add support for custom types of constant size.
In this case the generator produces a type alias.

Test: cargo test
Bug: 279494407
Change-Id: Ic1289083dbe2e3aaf3edea595fd6fc36a999c103
parent e1e32a6f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ rust_binary_host {
filegroup {
    name: "pdl_generated_files",
    srcs: [
        "tests/generated/custom_field_declaration_big_endian.rs",
        "tests/generated/custom_field_declaration_little_endian.rs",
        "tests/generated/enum_declaration_big_endian.rs",
        "tests/generated/enum_declaration_little_endian.rs",
        "tests/generated/packet_decl_8bit_enum_array_big_endian.rs",
@@ -79,6 +81,8 @@ filegroup {
        "tests/generated/packet_decl_child_packets_little_endian.rs",
        "tests/generated/packet_decl_complex_scalars_big_endian.rs",
        "tests/generated/packet_decl_complex_scalars_little_endian.rs",
        "tests/generated/packet_decl_custom_field_big_endian.rs",
        "tests/generated/packet_decl_custom_field_little_endian.rs",
        "tests/generated/packet_decl_empty_big_endian.rs",
        "tests/generated/packet_decl_empty_little_endian.rs",
        "tests/generated/packet_decl_fixed_enum_field_big_endian.rs",
+91 −0
Original line number Diff line number Diff line
@@ -899,6 +899,67 @@ fn generate_enum_decl(
    }
}

/// Generate the declaration for a custom field of static size.
///
/// * `id` - Enum identifier.
/// * `width` - Width of the backing type of the enum, in bits.
fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStream {
    let id = format_ident!("{}", id);
    let backing_type = types::Integer::new(width);
    let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width));
    let max_value = mask_bits(width, "usize");
    let common = quote! {
        impl From<&#id> for #backing_type {
            fn from(value: &#id) -> #backing_type {
                value.0
            }
        }

        impl From<#id> for #backing_type {
            fn from(value: #id) -> #backing_type {
                value.0
            }
        }
    };

    if backing_type.width == width {
        quote! {
            #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
            #[cfg_attr(feature = "serde", serde(from = #backing_type_str, into = #backing_type_str))]
            pub struct #id(#backing_type);

            #common

            impl From<#backing_type> for #id {
                fn from(value: #backing_type) -> Self {
                    #id(value)
                }
            }
        }
    } else {
        quote! {
            #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
            #[cfg_attr(feature = "serde", serde(try_from = #backing_type_str, into = #backing_type_str))]
            pub struct #id(#backing_type);

            #common

            impl TryFrom<#backing_type> for #id {
                type Error = #backing_type;
                fn try_from(value: #backing_type) -> std::result::Result<Self, Self::Error> {
                    if value > #max_value {
                        Err(value)
                    } else {
                        Ok(#id(value))
                    }
                }
            }
        }
    }
}

fn generate_decl(
    scope: &lint::Scope<'_>,
    file: &analyzer_ast::File,
@@ -919,6 +980,9 @@ fn generate_decl(
        ast::DeclDesc::Enum { id, tags, width } => {
            generate_enum_decl(id, tags, *width, false).to_string()
        }
        ast::DeclDesc::CustomField { id, width: Some(width), .. } => {
            generate_custom_field_decl(id, *width).to_string()
        }
        _ => todo!("unsupported Decl::{:?}", decl),
    }
}
@@ -1104,6 +1168,20 @@ mod tests {
        "#
    );

    test_pdl!(
        custom_field_declaration,
        r#"
        // Still unsupported.
        // custom_field Dynamic "dynamic"

        // Should generate a type with From<u32> implementation.
        custom_field ExactSize : 32 "exact_size"

        // Should generate a type with TryFrom<u32> implementation.
        custom_field TruncatedSize : 24 "truncated_size"
        "#
    );

    test_pdl!(
        packet_decl_simple_scalars,
        r#"
@@ -1260,6 +1338,19 @@ mod tests {
        "
    );

    test_pdl!(
        packet_decl_custom_field,
        r#"
          custom_field Bar1 : 24 "exact"
          custom_field Bar2 : 32 "truncated"

          packet Foo {
            a: Bar1,
            b: Bar2,
          }
        "#
    );

    test_pdl!(
        packet_decl_fixed_scalar_field,
        "
+28 −15
Original line number Diff line number Diff line
@@ -459,27 +459,40 @@ impl<'a> FieldParser<'a> {
        let id = format_ident!("{id}");
        let type_id = format_ident!("{type_id}");

        match self.scope.get_decl_width(decl, true) {
            None => self.code.push(quote! {
        self.code.push(match self.scope.get_decl_width(decl, true) {
            None => quote! {
                let #id = #type_id::parse_inner(&mut #span)?;
            }),
            },
            Some(width) => {
                assert_eq!(width % 8, 0, "Typedef field type size is not a multiple of 8");
                let width = syn::Index::from(width / 8);
                self.code.push(if let ast::DeclDesc::Checksum { .. } = &decl.desc {
                    // TODO: handle checksum fields.
                match &decl.desc {
                    ast::DeclDesc::Checksum { .. } => todo!(),
                    ast::DeclDesc::CustomField { .. } if [8, 16, 32, 64].contains(&width) => {
                        let get_uint = types::get_uint(self.endianness, width, span);
                        quote! {
                        #span.get_mut().advance(#width);
                            let #id = #get_uint.into();
                        }
                } else {
                    }
                    ast::DeclDesc::CustomField { .. } => {
                        let get_uint = types::get_uint(self.endianness, width, span);
                        quote! {
                            let #id = (#get_uint)
                                .try_into()
                                .unwrap(); // Value is masked and conversion must succeed.
                        }
                    }
                    ast::DeclDesc::Struct { .. } => {
                        let width = syn::Index::from(width / 8);
                        quote! {
                            let (head, tail) = #span.get().split_at(#width);
                            #span.replace(tail);
                            let #id = #type_id::parse(head)?;
                        }
                });
                    }
                    _ => unreachable!(),
                }
            }
        });
    }

    /// Parse body and payload fields.
+19 −2
Original line number Diff line number Diff line
@@ -302,8 +302,25 @@ impl<'a> FieldSerializer<'a> {

        let id = format_ident!("{id}");
        let span = format_ident!("{}", self.span);
        self.code.push(quote! {

        self.code.push(match &decl.desc {
            ast::DeclDesc::Checksum { .. } => todo!(),
            ast::DeclDesc::CustomField { width: Some(width), .. } => {
                let backing_type = types::Integer::new(*width);
                let put_uint = types::put_uint(
                    self.endianness,
                    &quote! { #backing_type::from(self.#id) },
                    *width,
                    self.span,
                );
                quote! {
                    #put_uint;
                }
            }
            ast::DeclDesc::Struct { .. } => quote! {
                self.#id.write_to(#span);
            },
            _ => unreachable!(),
        });
    }

+1 −0
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ pub fn rust_borrow(
        ast::FieldDesc::Typedef { type_id, .. } => match &scope.typedef[type_id].desc {
            ast::DeclDesc::Enum { .. } => quote!(),
            ast::DeclDesc::Struct { .. } => quote!(&),
            ast::DeclDesc::CustomField { .. } => quote!(&),
            desc => unreachable!("unexpected declaration: {desc:?}"),
        },
        ast::FieldDesc::Array { .. } => quote!(&),
Loading