chromium/mojo/public/tools/bindings/generators/rust_templates/struct.tmpl

{% from "enum.tmpl" import declare_enum -%}

{% macro declare_struct(name, struct) -%}

#[derive(Debug)]
pub struct {{name}} {
{%- for field in struct.fields %}
    pub r#{{field.name}}: {{field.kind|rust_field_type}},
{%- endfor %}
}

{%- set packed_fields = struct.packed.packed_fields %}

#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct {{name}}_Data {
    pub _header: bindings::data::StructHeader,
{%- for field in struct.packed|get_rust_data_fields%}
    pub r#{{field.name}}: {{field.type}},
{%- endfor %}
}

// Ensure that the Rust type's size is the same as mojo's.
static_assertions::assert_eq_size!(
    [u8; {{struct.versions[-1].num_bytes}}],
    {{name}}_Data,
);

impl {{name}}_Data {
    const KNOWN_VERSION_SIZES: &'static [(u32, u32)] =
        &[
{%- for version in struct.versions -%}
            ({{version.version}}, {{version.num_bytes}}),
{%- endfor -%}
        ];

    pub fn validate(
        #[allow(unused_variables)]
        ctx: &'_ mut bindings::ValidationContext<'_, '_>,
        chunk: &'_ bindings::MessageViewChunk<'_, '_, Self>,
    ) -> bindings::Result<()> {
        let data: Self = chunk.read();
        assert_eq!(chunk.len(), data._header.size as usize);

        // Ensure that the size and version match, or:
        // * if the version is newer than any known version, the size is not
        //   less than the last known version, or
        // * if the version is between known versions, the size is between their
        //   sizes (inclusive)
        for (version, size) in Self::KNOWN_VERSION_SIZES.iter().copied() {
            if data._header.version == version  {
                if data._header.size != size {
                    return Err(bindings::ValidationError::new(
                        bindings::ValidationErrorKind::UnexpectedStructHeader));
                }
                break;
            }

            if data._header.version > version && data._header.size < size {
                return Err(bindings::ValidationError::new(
                    bindings::ValidationErrorKind::UnexpectedStructHeader));
            }

            if data._header.version < version && data._header.size > size {
                return Err(bindings::ValidationError::new(
                    bindings::ValidationErrorKind::UnexpectedStructHeader));
            } else if data._header.version < version {
                break;
            }
        }

{%- macro validate_child(field_name, kind) %}
{%-   if kind|is_struct_kind %}
        if data.r#{{field_name}}.offset != 0 {
            let abs_ptr = bindings::AbsolutePointer::resolve_from(
                &data.r#{{field_name}},
                &data,
                chunk,
            )?;

            let child_chunk = ctx.claim_struct(abs_ptr)?;
            {{kind|rust_referent_data_type}}
                ::validate(ctx, &child_chunk)?;
        }
{%-   elif kind|is_enum_kind %}
        data.r#{{field_name}}.validate()?;
{%-   endif %}
{%- endmacro -%}

{% for packed_field in struct.packed.packed_fields -%}
{%-   set kind = packed_field.field.kind -%}
{%    if packed_field.linked_value_packed_field  -%}
{#-     The current field is a packed boolean which determines if its associated
        value field is present. Process both. The loop will iterate over the value
        field later, but it will be skipped. -#}
{%-     set orig_field = packed_field.original_field -%}
{%-     set value_field = packed_field.linked_value_packed_field -%}
{%-     set flag_location = packed_field|get_packed_bool_location -%}
        // {{orig_field.name}} is nullable and has an associated boolean flag.
        if (data.r#{{flag_location.field_name}}
          & 1u8 << {{flag_location.bit_offset}}) > 0 {
            {{validate_child(orig_field.name, value_field.field.kind)}}
        }
{%-   elif packed_field.original_field -%}
{#-     Skip field: it was already processed above. -#}
{%-   elif kind|is_pointer_kind and not kind|is_nullable_kind -%}
        if data.r#{{packed_field.field.name}}.offset == 0 {
            return Err(bindings::ValidationError::new(
                bindings::ValidationErrorKind::UnexpectedNullPointer));
        }

        {{validate_child(packed_field.field.name, packed_field.field.kind)}}
{%-   else -%}
        {{validate_child(packed_field.field.name, packed_field.field.kind)}}
{%-   endif %}
{%- endfor %}

        Ok(())
    }
}

{%- for enum in struct.enums %}
{{declare_enum(name + "_" + enum.name, enum)}}
{%- endfor %}

{%- endmacro -%}