//! Code generation for parsed SBE schemas.
//!
//! The generator produces one Rust module per message along with a
//! `types.rs` module which contains definitions for user defined
//! enums, sets and composites.  A `mod.rs` file is also emitted to
//! re‑export the generated types for ergonomic use.

use crate::GeneratorOptions;
use crate::parser::{
    CompositeField, Field, Group, GroupMember, Message, MessageMember, Schema, TypeDef,
    VarDataField,
};
use heck::ToSnakeCase;
use std::collections::HashSet;

const PARSE_PREFIX_METHOD: &str = "    #[inline]\n    pub fn parse_prefix(body: &[u8]) -> Option<(&Self, &[u8])> {\n        Ref::<_, Self>::from_prefix(body)\n            .ok()\n            .map(|(r, b)| (Ref::into_ref(r), b))\n    }\n";

fn push_doc_comment(buf: &mut String, text: &str) {
    for line in text.lines() {
        buf.push_str("/// ");
        buf.push_str(line.trim_end());
        buf.push('\n');
    }
}

fn maybe_doc_comment(buf: &mut String, desc: &Option<String>) {
    if let Some(text) = desc.as_deref() {
        push_doc_comment(buf, text);
    }
}

fn push_file_preamble(buf: &mut String, opts: &GeneratorOptions, default_allow: Option<&str>) {
    if let Some(allow) = opts.allow_attr.as_deref() {
        buf.push_str(allow);
        if !allow.ends_with('\n') {
            buf.push('\n');
        }
    }
    buf.push_str("// Automatically generated by sbe_gen.\n");
    if let Some(allow) = default_allow {
        buf.push_str(allow);
        if !allow.ends_with('\n') {
            buf.push('\n');
        }
    }
}

fn message_fields(msg: &Message) -> Vec<&Field> {
    msg.members
        .iter()
        .filter_map(|m| {
            if let MessageMember::Field(f) = m {
                Some(f)
            } else {
                None
            }
        })
        .collect()
}

fn message_groups(msg: &Message) -> Vec<&Group> {
    msg.members
        .iter()
        .filter_map(|m| {
            if let MessageMember::Group(g) = m {
                Some(g)
            } else {
                None
            }
        })
        .collect()
}

fn message_data(msg: &Message) -> Vec<&VarDataField> {
    msg.members
        .iter()
        .filter_map(|m| {
            if let MessageMember::Data(d) = m {
                Some(d)
            } else {
                None
            }
        })
        .collect()
}

fn group_fields(g: &Group) -> Vec<&Field> {
    g.members
        .iter()
        .filter_map(|m| {
            if let GroupMember::Field(f) = m {
                Some(f)
            } else {
                None
            }
        })
        .collect()
}

fn group_data(g: &Group) -> Vec<&VarDataField> {
    g.members
        .iter()
        .filter_map(|m| {
            if let GroupMember::Data(d) = m {
                Some(d)
            } else {
                None
            }
        })
        .collect()
}

fn group_groups(g: &Group) -> Vec<&Group> {
    g.members
        .iter()
        .filter_map(|m| {
            if let GroupMember::Group(gr) = m {
                Some(gr)
            } else {
                None
            }
        })
        .collect()
}

fn collect_used_types(schema: &Schema) -> HashSet<String> {
    let mut used = HashSet::new();
    for msg in &schema.messages {
        collect_used_types_in_message(msg, &mut used);
    }
    for ty in schema.types.values() {
        if let TypeDef::Composite { fields, .. } = ty {
            for field in fields {
                if let CompositeField::Ref { ty, .. } = field {
                    used.insert(ty.clone());
                }
            }
        }
    }
    used
}

fn collect_schema_fields(schema: &Schema) -> Vec<&Field> {
    let mut out = Vec::new();
    for msg in &schema.messages {
        collect_message_fields(msg, &mut out);
    }
    out
}

fn collect_message_fields<'a>(msg: &'a Message, out: &mut Vec<&'a Field>) {
    for member in &msg.members {
        match member {
            MessageMember::Field(field) => out.push(field),
            MessageMember::Group(group) => collect_group_fields(group, out),
            MessageMember::Data(_) => {}
        }
    }
}

fn collect_group_fields<'a>(group: &'a Group, out: &mut Vec<&'a Field>) {
    for member in &group.members {
        match member {
            GroupMember::Field(field) => out.push(field),
            GroupMember::Group(nested) => collect_group_fields(nested, out),
            GroupMember::Data(_) => {}
        }
    }
}

fn constant_type_alias_from_schema(type_name: &str, schema: &Schema) -> Option<String> {
    let mut candidates = HashSet::new();
    for field in collect_schema_fields(schema) {
        if field.ty != type_name {
            continue;
        }
        if let Some(value_ref) = &field.value_ref
            && let Some((ty, _)) = value_ref.split_once('.')
            && ty != type_name
            && let Some(td) = schema.types.get(ty)
            && !matches!(td, TypeDef::Composite { .. })
        {
            candidates.insert(ty.to_string());
        }
        if field.name != type_name
            && let Some(td) = schema.types.get(&field.name)
            && !matches!(td, TypeDef::Composite { .. })
        {
            candidates.insert(field.name.clone());
        }
    }
    if candidates.len() == 1 {
        candidates.into_iter().next()
    } else {
        None
    }
}

fn collect_used_types_in_message(msg: &Message, used: &mut HashSet<String>) {
    for member in &msg.members {
        match member {
            MessageMember::Field(field) => {
                used.insert(field.ty.clone());
            }
            MessageMember::Group(group) => collect_used_types_in_group(group, used),
            MessageMember::Data(data) => {
                used.insert(data.ty.clone());
            }
        }
    }
}

fn collect_used_types_in_group(group: &Group, used: &mut HashSet<String>) {
    used.insert(group.dimension_type.clone());
    for member in &group.members {
        match member {
            GroupMember::Field(field) => {
                used.insert(field.ty.clone());
            }
            GroupMember::Group(nested) => collect_used_types_in_group(nested, used),
            GroupMember::Data(data) => {
                used.insert(data.ty.clone());
            }
        }
    }
}

fn group_has_var_data(g: &Group) -> bool {
    if !group_data(g).is_empty() {
        return true;
    }
    for sub in group_groups(g) {
        if group_has_var_data(sub) {
            return true;
        }
    }
    false
}

/// Generate a collection of `(filename, contents)` tuples for the
/// supplied schema.
pub fn generate(schema: &Schema, opts: &GeneratorOptions) -> Vec<(String, String)> {
    let mut files = Vec::new();
    // always emit a types module
    let types_code = generate_types(schema, opts);
    files.push(("types.rs".to_string(), types_code));
    // emit standard SBE message header
    files.push((
        "message_header.rs".to_string(),
        generate_message_header(opts),
    ));
    // emit one file per message
    for msg in &schema.messages {
        let code = generate_message(msg, schema, opts);
        let fname = format!("{}.rs", msg.name.to_snake_case());
        files.push((fname, code));
    }
    // emit a mod.rs which links everything together
    let mod_code = generate_mod_rs(schema, opts);
    files.push(("mod.rs".to_string(), mod_code));
    files
}

/// Generate the content of `mod.rs` re‑exporting messages and types.
fn generate_mod_rs(schema: &Schema, opts: &GeneratorOptions) -> String {
    let mut mod_lines = String::new();
    push_file_preamble(
        &mut mod_lines,
        opts,
        Some("#![allow(dead_code, non_camel_case_types)]"),
    );
    mod_lines.push('\n');
    mod_lines.push_str("pub mod types;\n");
    mod_lines.push_str("pub mod message_header;\n");
    for msg in &schema.messages {
        let module = msg.name.to_snake_case();
        mod_lines.push_str(&format!("pub mod {};\n", module));
    }
    mod_lines.push('\n');
    mod_lines.push_str("// Re‑export message types\n");
    mod_lines.push_str("pub use message_header::MessageHeader;\n");
    for msg in &schema.messages {
        let module = msg.name.to_snake_case();
        mod_lines.push_str(&format!("pub use {}::{};\n", module, msg.name));
        mod_lines.push_str(&format!("pub use {}::{}Builder;\n", module, msg.name));
    }
    mod_lines
}

/// Generate the `types.rs` module containing definitions of enums, sets and composites.
fn generate_types(schema: &Schema, opts: &GeneratorOptions) -> String {
    let mut code = String::new();
    let types = &schema.types;
    let used_types = collect_used_types(schema);
    push_file_preamble(
        &mut code,
        opts,
        Some("#![allow(dead_code, non_camel_case_types)]"),
    );
    code.push_str(
        "use zerocopy::{Ref, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned};\n",
    );
    code.push_str(&format!(
        "use zerocopy::byteorder::{}_endian::*;\n",
        opts.endian
    ));
    code.push('\n');
    // for each type
    let mut sorted: Vec<_> = types.values().collect();
    // sort by name for deterministic output
    sorted.sort_by_key(|t| match t {
        TypeDef::Primitive { name, .. } => name.clone(),
        TypeDef::Enum { name, .. } => name.clone(),
        TypeDef::Set { name, .. } => name.clone(),
        TypeDef::Composite { name, .. } => name.clone(),
    });
    for ty in sorted {
        match ty {
            TypeDef::Primitive {
                name,
                primitive,
                length,
                presence,
                null_value,
                constant,
                description,
            } => {
                let rust_type = primitive_to_rust(primitive, opts);
                if rust_type.is_none() {
                    continue;
                }
                let rust_type = rust_type.unwrap();
                maybe_doc_comment(&mut code, description);
                let is_constant = presence.as_deref() == Some("constant");
                let is_optional = presence.as_deref() == Some("optional");
                let mut has_alias = false;
                if let Some(len) = length {
                    code.push_str(&format!("pub type {} = [{}; {}];\n", name, rust_type, len));
                    has_alias = true;
                } else if presence.as_deref() == Some("constant") {
                    if used_types.contains(name) {
                        let alias = opts
                            .constant_type_aliases
                            .get(name)
                            .cloned()
                            .or_else(|| constant_type_alias_from_schema(name, schema))
                            .unwrap_or_else(|| rust_type.clone());
                        code.push_str(&format!("pub type {} = {};\n", name, alias));
                        has_alias = true;
                    } else if let Some(value) = constant {
                        if let Some(expr) = const_scalar_expr(primitive, &rust_type, value) {
                            code.push_str(&format!(
                                "pub const {}: {} = {};\n",
                                name, rust_type, expr
                            ));
                        } else {
                            code.push_str(&format!(
                                "pub const {}: {} = {} as {};\n",
                                name, rust_type, value, rust_type
                            ));
                        }
                    } else {
                        code.push_str(&format!(
                            "pub const {}: {} = {} as {};\n",
                            name,
                            rust_type,
                            primitive_default_value(primitive),
                            rust_type
                        ));
                    }
                } else {
                    code.push_str(&format!("pub type {} = {};\n", name, rust_type));
                    has_alias = true;
                }

                if has_alias && !is_constant && (null_value.is_some() || is_optional) {
                    let null_raw = null_value.as_deref().or_else(|| {
                        if is_optional && length.is_none() {
                            optional_null_literal(primitive)
                        } else {
                            None
                        }
                    });
                    if let Some(null_raw) = null_raw {
                        let cname = format!("{}_NULL", name.to_uppercase());
                        if let Some(len) = length {
                            if let Some((_, expr)) =
                                const_array_expr(primitive, &rust_type, null_raw, *len)
                            {
                                code.push_str(&format!(
                                    "pub const {}: {} = {};\n",
                                    cname, name, expr
                                ));
                            }
                        } else if let Some(expr) =
                            const_scalar_expr(primitive, &rust_type, null_raw)
                        {
                            code.push_str(&format!("pub const {}: {} = {};\n", cname, name, expr));
                        }
                    }
                }
            }
            TypeDef::Enum {
                name,
                encoding,
                values,
                description,
            } => {
                let primitive = encoding_primitive(encoding, types);
                let rust_type = primitive
                    .as_deref()
                    .and_then(|p| primitive_to_rust(p, opts))
                    .or_else(|| primitive_to_rust(encoding, opts))
                    .unwrap_or_else(|| "u8".into());
                let repr_ty = primitive
                    .as_deref()
                    .and_then(optional_host_type)
                    .unwrap_or("u8");
                let prim_is_char = primitive.as_deref() == Some("char");
                maybe_doc_comment(&mut code, description);
                code.push_str("#[repr(transparent)]\n");
                code.push_str(
                    "#[derive(Debug, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy, PartialEq, Eq)]\n",
                );
                code.push_str(&format!("pub struct {}(pub {});\n", name, rust_type));
                code.push_str(&format!("impl {} {{\n", name));
                code.push_str(PARSE_PREFIX_METHOD);
                code.push_str("}\n\n");
                if !values.is_empty() {
                    let enum_name = format!("{}Enum", name);
                    maybe_doc_comment(&mut code, description);
                    code.push_str(&format!("#[repr({})]\n", repr_ty));
                    code.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n");
                    code.push_str(&format!("pub enum {} {{\n", enum_name));
                    for (vname, val, vdesc) in values {
                        let literal = if prim_is_char {
                            if val.len() == 1 {
                                let c = val.chars().next().unwrap();
                                format!("{}u8", c as u32)
                            } else {
                                val.clone()
                            }
                        } else {
                            val.clone()
                        };
                        maybe_doc_comment(&mut code, vdesc);
                        code.push_str(&format!("    {} = {},\n", vname, literal));
                    }
                    code.push_str("}\n");
                }
                // define associated constants for variants
                if !values.is_empty() {
                    code.push_str(&format!("impl {} {{\n", name));
                    for (vname, val, vdesc) in values {
                        // convert char values to numeric if necessary
                        let literal = if prim_is_char {
                            if val.len() == 1 {
                                let c = val.chars().next().unwrap();
                                format!("{}u8", c as u32)
                            } else {
                                val.clone()
                            }
                        } else {
                            val.clone()
                        };
                        maybe_doc_comment(&mut code, vdesc);
                        code.push_str(&format!(
                            "    pub const {}: Self = Self({});\n",
                            vname,
                            builder_value_expr(&literal, &rust_type)
                        ));
                    }
                    if let Some(raw_expr) = scalar_expr("self.0", &rust_type) {
                        let enum_name = format!("{}Enum", name);
                        code.push_str(&format!(
                            "    pub fn as_enum(self) -> Option<{enum_name}> {{\n        let raw = {raw_expr};\n        match raw {{\n",
                            enum_name = enum_name,
                            raw_expr = raw_expr
                        ));
                        for (vname, val, _) in values {
                            let literal = if prim_is_char {
                                if val.len() == 1 {
                                    let c = val.chars().next().unwrap();
                                    format!("{}u8", c as u32)
                                } else {
                                    val.clone()
                                }
                            } else {
                                val.clone()
                            };
                            code.push_str(&format!(
                                "            {lit} => Some({enum_name}::{vname}),\n",
                                lit = literal,
                                enum_name = enum_name,
                                vname = vname
                            ));
                        }
                        code.push_str("            _ => None,\n        }\n    }\n");
                    }
                    code.push_str("}\n");
                    let enum_name = format!("{}Enum", name);
                    code.push_str(&format!("impl From<{}> for {} {{\n", enum_name, name));
                    code.push_str(&format!(
                        "    fn from(v: {}) -> Self {{\n        let raw: {} = v as {};\n        let encoded = {};\n        Self(encoded)\n    }}\n",
                        enum_name,
                        repr_ty,
                        repr_ty,
                        builder_value_expr("raw", &rust_type)
                    ));
                    code.push_str("}\n");
                }
                code.push('\n');
            }
            TypeDef::Set {
                name,
                encoding,
                choices,
                description,
            } => {
                let primitive = encoding_primitive(encoding, types);
                let rust_type = primitive
                    .as_deref()
                    .and_then(|p| primitive_to_rust(p, opts))
                    .or_else(|| primitive_to_rust(encoding, opts))
                    .unwrap_or_else(|| "u8".into());
                maybe_doc_comment(&mut code, description);
                code.push_str("#[repr(transparent)]\n");
                code.push_str(
                    "#[derive(Debug, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy, PartialEq, Eq)]\n",
                );
                code.push_str(&format!("pub struct {}(pub {});\n", name, rust_type));
                code.push_str(&format!("impl {} {{\n", name));
                code.push_str(PARSE_PREFIX_METHOD);
                code.push_str("}\n\n");
                if !choices.is_empty() {
                    code.push_str(&format!("impl {} {{\n", name));
                    for (cname, bit, cdesc) in choices {
                        // bit may be decimal or integer string
                        let bit_idx: u32 = bit.parse().unwrap_or(0);
                        maybe_doc_comment(&mut code, cdesc);
                        code.push_str(&format!(
                            "    pub const {}: Self = Self({});\n",
                            cname,
                            set_bit_value_expr(bit_idx, &rust_type)
                        ));
                    }
                    code.push_str("}\n");
                }
                code.push('\n');
            }
            TypeDef::Composite {
                name,
                fields,
                description,
            } => {
                // attach doc comment if present on composite
                maybe_doc_comment(&mut code, description);
                // generate a struct representing the composite
                code.push_str("#[repr(C)]\n");
                code.push_str(
                    "#[derive(Debug, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy)]\n",
                );
                code.push_str(&format!("pub struct {} {{\n", name));
                let mut const_fields = Vec::new();
                for f in fields {
                    match f {
                        CompositeField::Type {
                            name: fname,
                            primitive,
                            length,
                            presence,
                            constant,
                            description,
                            ..
                        } => {
                            if presence.as_deref() == Some("constant") {
                                if let Some(val) = constant {
                                    const_fields.push((
                                        fname.clone(),
                                        primitive.clone(),
                                        *length,
                                        val.clone(),
                                    ));
                                }
                                continue;
                            }
                            if let Some(rust_type) = primitive_to_rust(primitive, opts) {
                                if let Some(len) = length {
                                    maybe_doc_comment(&mut code, description);
                                    code.push_str(&format!(
                                        "    pub {}: [{}; {}],\n",
                                        fname.to_snake_case(),
                                        rust_type,
                                        len
                                    ));
                                } else {
                                    maybe_doc_comment(&mut code, description);
                                    code.push_str(&format!(
                                        "    pub {}: {},\n",
                                        fname.to_snake_case(),
                                        rust_type
                                    ));
                                }
                            }
                        }
                        CompositeField::Ref { name: fname, ty } => {
                            code.push_str(&format!("    pub {}: {},\n", fname.to_snake_case(), ty));
                        }
                    }
                }
                code.push_str("}\n\n");
                code.push_str(&format!("impl {} {{\n", name));
                code.push_str(PARSE_PREFIX_METHOD);
                code.push_str("}\n\n");
                let mut impl_body = String::new();
                if !const_fields.is_empty() {
                    for (fname, prim, len, val) in const_fields {
                        if let Some(rust_type) = primitive_to_rust(&prim, opts) {
                            let cname = fname.to_uppercase();
                            if let Some(len) = len
                                && len > 1
                            {
                                if let Some((ty, expr)) =
                                    const_array_expr(&prim, &rust_type, &val, len)
                                {
                                    impl_body.push_str(&format!(
                                        "    pub const {}: {} = {};\n",
                                        cname, ty, expr
                                    ));
                                }
                                continue;
                            }
                            if let Some(expr) = const_scalar_expr(&prim, &rust_type, &val) {
                                impl_body.push_str(&format!(
                                    "    pub const {}: {} = {};\n",
                                    cname, rust_type, expr
                                ));
                            }
                        }
                    }
                }
                composite_null_constants(&mut impl_body, fields, schema, opts);
                optional_methods_for_composite_fields(&mut impl_body, fields, schema, opts, "self");
                if !impl_body.is_empty() {
                    code.push_str(&format!("impl {} {{\n", name));
                    code.push_str(&impl_body);
                    code.push_str("}\n\n");
                }
            }
        }
    }
    code
}

/// Generate the standard SBE message header module (block length, template id, schema id, version).
fn generate_message_header(opts: &GeneratorOptions) -> String {
    let mut code = String::new();
    push_file_preamble(
        &mut code,
        opts,
        Some("#![allow(dead_code, non_camel_case_types)]"),
    );
    code.push_str(
        "use zerocopy::{Ref, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned};\n",
    );
    code.push_str(&format!(
        "use zerocopy::byteorder::{}_endian::*;\n",
        opts.endian
    ));
    code.push('\n');
    code.push_str("#[repr(C)]\n");
    code.push_str(
        "#[derive(Debug, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy)]\n",
    );
    code.push_str("pub struct MessageHeader {\n    pub block_length: U16,\n    pub template_id: U16,\n    pub schema_id: U16,\n    pub version: U16,\n}\n\n");
    code.push_str("impl MessageHeader {\n");
    code.push_str(PARSE_PREFIX_METHOD);
    code.push_str("}\n");
    code
}

/// Generate the module for a single message.  This includes imports,
/// macro definition and the message struct itself.  Fields using
/// unsupported types are skipped.
fn generate_message(msg: &Message, schema: &Schema, opts: &GeneratorOptions) -> String {
    let fields = message_fields(msg);
    let groups = message_groups(msg);
    let data_fields = message_data(msg);
    let has_group_data = groups.iter().any(|g| group_has_var_data(g));
    let has_var_data = has_group_data || !data_fields.is_empty();
    let msg_block_length = message_block_length(msg, schema, opts);
    let field_layouts = layout_fields(&fields, schema, opts);
    let _offsets_present = fields.iter().any(|f| f.offset.is_some());
    let mut code = String::new();
    push_file_preamble(&mut code, opts, None);
    code.push_str(
        "use zerocopy::{Ref, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned};\n",
    );
    code.push_str(&format!(
        "use zerocopy::byteorder::{}_endian::*;\n",
        opts.endian
    ));
    code.push_str("use crate::types::*;\n");
    code.push_str("use crate::message_header::MessageHeader;\n\n");
    if has_var_data {
        code.push_str("use core::str;\n\n");
    }
    code.push_str(&format!("const ENDIAN: &str = \"{}\";\n\n", opts.endian));
    code.push_str(
        "#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum EncodeIntoError {\n    BufferTooSmall { required: usize, available: usize },\n    LengthOverflow { len: usize, max: usize },\n    InvalidState(&'static str),\n}\n\n",
    );
    if has_var_data {
        code.push_str("#[derive(Debug, Clone, Copy)]\n");
        code.push_str(
            "pub struct VarData<'a> {\n    pub len: usize,\n    pub bytes: &'a [u8],\n}\n\n",
        );
        code.push_str("impl<'a> VarData<'a> {\n    pub fn as_str(&self) -> Option<&'a str> { str::from_utf8(self.bytes).ok() }\n}\n\n");
        code.push_str("#[derive(Clone, Copy)]\n");
        code.push_str("enum LengthKind { U8, U16, U32, U64 }\n\n");
        code.push_str(
            "#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct EncodeError {\n    pub len: usize,\n    pub max: usize,\n}\n\n",
        );
        code.push_str(
            "fn length_max(kind: LengthKind) -> usize {\n    match kind {\n        LengthKind::U8 => u8::MAX as usize,\n        LengthKind::U16 => u16::MAX as usize,\n        LengthKind::U32 => u32::MAX as usize,\n        LengthKind::U64 => u64::MAX as usize,\n    }\n}\n\n",
        );
        code.push_str("fn read_length(kind: LengthKind, buf: &[u8]) -> Option<(usize, &[u8])> {\n    match kind {\n        LengthKind::U8 => {\n            let (&b, rest) = buf.split_first()?;\n            Some((b as usize, rest))\n        }\n        LengthKind::U16 => {\n            let (v, rest) = Ref::<_, U16>::from_prefix(buf).ok()?;\n            Some((v.get() as usize, rest))\n        }\n        LengthKind::U32 => {\n            let (v, rest) = Ref::<_, U32>::from_prefix(buf).ok()?;\n            Some((v.get() as usize, rest))\n        }\n        LengthKind::U64 => {\n            let (v, rest) = Ref::<_, U64>::from_prefix(buf).ok()?;\n            Some((v.get() as usize, rest))\n        }\n    }\n}\n\n");
        code.push_str("fn parse_var_data<'a>(buf: &'a [u8], kind: LengthKind) -> Option<(VarData<'a>, &'a [u8])> {\n    let (len, rest) = read_length(kind, buf)?;\n    if rest.len() < len { return None; }\n    let (bytes, tail) = rest.split_at(len);\n    Some((VarData { len, bytes }, tail))\n}\n\n");
        code.push_str("fn write_length(buf: &mut Vec<u8>, len: usize, kind: LengthKind, endian: &str) -> Result<(), EncodeError> {\n    let max = length_max(kind);\n    if len > max { return Err(EncodeError { len, max }); }\n    match kind {\n        LengthKind::U8 => buf.push(len as u8),\n        LengthKind::U16 => {\n            let v = len as u16;\n            if endian == \"big\" { buf.extend_from_slice(&v.to_be_bytes()); } else { buf.extend_from_slice(&v.to_le_bytes()); }\n        }\n        LengthKind::U32 => {\n            let v = len as u32;\n            if endian == \"big\" { buf.extend_from_slice(&v.to_be_bytes()); } else { buf.extend_from_slice(&v.to_le_bytes()); }\n        }\n        LengthKind::U64 => {\n            let v = len as u64;\n            if endian == \"big\" { buf.extend_from_slice(&v.to_be_bytes()); } else { buf.extend_from_slice(&v.to_le_bytes()); }\n        }\n    }\n    Ok(())\n}\n\n");
        code.push_str("fn write_var_data(buf: &mut Vec<u8>, bytes: &[u8], kind: LengthKind, endian: &str) -> Result<(), EncodeError> {\n    write_length(buf, bytes.len(), kind, endian)?;\n    buf.extend_from_slice(bytes);\n    Ok(())\n}\n\n");
        code.push_str("fn write_length_into(dst: &mut [u8], offset: usize, len: usize, kind: LengthKind, endian: &str) -> Result<usize, EncodeIntoError> {\n    let max = length_max(kind);\n    if len > max { return Err(EncodeIntoError::LengthOverflow { len, max }); }\n    match kind {\n        LengthKind::U8 => {\n            let end = offset.checked_add(1).ok_or(EncodeIntoError::InvalidState(\"length offset overflow\"))?;\n            if end > dst.len() { return Err(EncodeIntoError::BufferTooSmall { required: end, available: dst.len() }); }\n            dst[offset] = len as u8;\n            Ok(1)\n        }\n        LengthKind::U16 => {\n            let end = offset.checked_add(2).ok_or(EncodeIntoError::InvalidState(\"length offset overflow\"))?;\n            if end > dst.len() { return Err(EncodeIntoError::BufferTooSmall { required: end, available: dst.len() }); }\n            let v = len as u16;\n            if endian == \"big\" { dst[offset..end].copy_from_slice(&v.to_be_bytes()); } else { dst[offset..end].copy_from_slice(&v.to_le_bytes()); }\n            Ok(2)\n        }\n        LengthKind::U32 => {\n            let end = offset.checked_add(4).ok_or(EncodeIntoError::InvalidState(\"length offset overflow\"))?;\n            if end > dst.len() { return Err(EncodeIntoError::BufferTooSmall { required: end, available: dst.len() }); }\n            let v = len as u32;\n            if endian == \"big\" { dst[offset..end].copy_from_slice(&v.to_be_bytes()); } else { dst[offset..end].copy_from_slice(&v.to_le_bytes()); }\n            Ok(4)\n        }\n        LengthKind::U64 => {\n            let end = offset.checked_add(8).ok_or(EncodeIntoError::InvalidState(\"length offset overflow\"))?;\n            if end > dst.len() { return Err(EncodeIntoError::BufferTooSmall { required: end, available: dst.len() }); }\n            let v = len as u64;\n            if endian == \"big\" { dst[offset..end].copy_from_slice(&v.to_be_bytes()); } else { dst[offset..end].copy_from_slice(&v.to_le_bytes()); }\n            Ok(8)\n        }\n    }\n}\n\n");
        code.push_str("fn write_var_data_into(dst: &mut [u8], offset: usize, bytes: &[u8], kind: LengthKind, endian: &str) -> Result<usize, EncodeIntoError> {\n    let prefix = write_length_into(dst, offset, bytes.len(), kind, endian)?;\n    let data_start = offset.checked_add(prefix).ok_or(EncodeIntoError::InvalidState(\"var data offset overflow\"))?;\n    let data_end = data_start.checked_add(bytes.len()).ok_or(EncodeIntoError::InvalidState(\"var data length overflow\"))?;\n    if data_end > dst.len() { return Err(EncodeIntoError::BufferTooSmall { required: data_end, available: dst.len() }); }\n    dst[data_start..data_end].copy_from_slice(bytes);\n    Ok(prefix + bytes.len())\n}\n\n");
    }
    code.push_str(
        "fn ensure_len(buf: &mut Vec<u8>, len: usize) {\n    if buf.len() < len { buf.resize(len, 0); }\n}\n\n",
    );
    code.push_str(
        "fn write_bytes_at<T: IntoBytes + Immutable>(buf: &mut Vec<u8>, offset: usize, value: &T) {\n    let bytes = value.as_bytes();\n    ensure_len(buf, offset + bytes.len());\n    buf[offset..offset + bytes.len()].copy_from_slice(bytes);\n}\n\n",
    );
    code.push_str(
        "fn write_bytes_into<T: IntoBytes + Immutable>(dst: &mut [u8], offset: usize, value: &T) -> Result<(), EncodeIntoError> {\n    let bytes = value.as_bytes();\n    let end = offset.checked_add(bytes.len()).ok_or(EncodeIntoError::InvalidState(\"fixed field offset overflow\"))?;\n    if end > dst.len() { return Err(EncodeIntoError::BufferTooSmall { required: end, available: dst.len() }); }\n    dst[offset..end].copy_from_slice(bytes);\n    Ok(())\n}\n\n",
    );
    // struct definition
    code.push_str("#[repr(C)]\n");
    code.push_str(
        "#[derive(Debug, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy)]\n",
    );
    code.push_str(&format!("pub struct {} {{\n", msg.name));
    write_fields_with_offsets(&mut code, &fields, schema, opts);
    code.push_str("}\n\n");
    // associated constants for constant fields
    push_constant_field_impl(&mut code, &msg.name, &fields, schema, opts);
    // impl parse_prefix
    code.push_str(&format!("impl {} {{\n", msg.name));
    code.push_str(PARSE_PREFIX_METHOD);
    code.push_str(&format!(
        "    pub const BLOCK_LENGTH: u16 = {};\n",
        msg_block_length
    ));
    code.push_str(&format!("    pub const TEMPLATE_ID: u16 = {};\n", msg.id));
    let schema_id = schema.schema_id.unwrap_or(0);
    let schema_version = schema.version.unwrap_or(0);
    code.push_str(&format!("    pub const SCHEMA_ID: u16 = {};\n", schema_id));
    code.push_str(&format!(
        "    pub const SCHEMA_VERSION: u16 = {};\n",
        schema_version
    ));
    optional_methods_for_fields(&mut code, &fields, schema, opts, "self");
    // message metadata
    let msg_since = msg.since_version.unwrap_or(0);
    let msg_sem = msg.semantic_type.clone().unwrap_or_default();
    code.push_str(&format!(
        "    pub const SINCE_VERSION: u32 = {};\n",
        msg_since
    ));
    code.push_str(&format!(
        "    pub const SEMANTIC_TYPE: &'static str = \"{}\";\n",
        msg_sem
    ));
    // field metadata
    for f in &fields {
        let sv = f.since_version.unwrap_or(0);
        let sem = f.semantic_type.clone().unwrap_or_default();
        if let Some(off) = f.offset {
            code.push_str(&format!(
                "    pub const {}_OFFSET: u32 = {};\n",
                f.name.to_uppercase(),
                off
            ));
        }
        if let Some(ref minv) = f.min_value {
            code.push_str(&format!(
                "    pub const {}_MIN: &str = \"{}\";\n",
                f.name.to_uppercase(),
                minv
            ));
        }
        if let Some(ref maxv) = f.max_value {
            code.push_str(&format!(
                "    pub const {}_MAX: &str = \"{}\";\n",
                f.name.to_uppercase(),
                maxv
            ));
        }
        if let Some(ref nullv) = f.null_value {
            code.push_str(&format!(
                "    pub const {}_NULL: &str = \"{}\";\n",
                f.name.to_uppercase(),
                nullv
            ));
        }
        if let Some(ref initv) = f.initial_value {
            code.push_str(&format!(
                "    pub const {}_INITIAL: &str = \"{}\";\n",
                f.name.to_uppercase(),
                initv
            ));
        }
        code.push_str(&format!(
            "    pub const {}_SINCE_VERSION: u32 = {};\n",
            f.name.to_uppercase(),
            sv
        ));
        code.push_str(&format!(
            "    pub const {}_SEMANTIC_TYPE: &'static str = \"{}\";\n",
            f.name.to_uppercase(),
            sem
        ));
    }
    if !data_fields.is_empty() {
        for d in &data_fields {
            let fn_name = d.name.to_snake_case();
            let kind = length_kind_token(length_kind_for_data(&d.ty, schema, opts));
            code.push_str(&format!(
                "    pub fn parse_{}<'a>(&self, buf: &'a [u8]) -> Option<(VarData<'a>, &'a [u8])> {{\n        parse_var_data(buf, {})\n    }}\n",
                fn_name, kind
            ));
        }
    }
    code.push_str("}\n\n");
    let builder_name = format!("{}Builder", msg.name);
    let encoder_name = format!("{}Encoder", msg.name);
    let fixed_required = msg_block_length.max(
        field_layouts
            .iter()
            .map(|layout| layout.offset + layout.size)
            .max()
            .unwrap_or(0),
    );
    code.push_str(&format!(
        "pub struct {} {{\n    buf: Vec<u8>,\n}}\n\n",
        builder_name
    ));
    code.push_str(&format!("impl {} {{\n", builder_name));
    code.push_str(&format!(
        "    pub const BLOCK_LENGTH: u16 = {}::BLOCK_LENGTH;\n",
        msg.name
    ));
    code.push_str(&format!(
        "    pub const TEMPLATE_ID: u16 = {}::TEMPLATE_ID;\n",
        msg.name
    ));
    code.push_str(&format!(
        "    pub const SCHEMA_ID: u16 = {}::SCHEMA_ID;\n",
        msg.name
    ));
    code.push_str(&format!(
        "    pub const SCHEMA_VERSION: u16 = {}::SCHEMA_VERSION;\n",
        msg.name
    ));
    code.push_str(
        "    pub fn new() -> Self {\n        Self { buf: vec![0u8; Self::BLOCK_LENGTH as usize] }\n    }\n",
    );
    code.push_str(
        "    pub fn with_capacity(capacity: usize) -> Self {\n        let mut buf = vec![0u8; Self::BLOCK_LENGTH as usize];\n        buf.reserve(capacity);\n        Self { buf }\n    }\n",
    );
    for layout in &field_layouts {
        let field = layout.field;
        if field_is_constant(field, schema) {
            continue;
        }
        let offset = layout.offset;
        let field_name = field.name.to_snake_case();
        if let Some(resolved_type) =
            resolve_type(&field.ty, schema, opts, field.byte_order.as_deref())
        {
            let param_ty = builder_param_type(&resolved_type);
            let encoded_expr = builder_value_expr("value", &resolved_type);
            let needs_checks = field.min_value.is_some()
                || field.max_value.is_some()
                || (field.presence.as_deref() != Some("optional") && field.null_value.is_some());
            if needs_checks {
                if let (Some(host_ty), Some(raw_expr)) = (
                    host_type_for_field(field, schema),
                    raw_expr_for_value(field, "value", schema, opts),
                ) {
                    code.push_str(&format!("    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let raw: {host} = {raw};\n", name = field_name, param = param_ty, host = host_ty, raw = raw_expr));
                    if let Some(ref minv) = field.min_value {
                        code.push_str(&format!(
                            "        let min: {host} = \"{minv}\".parse().expect(\"minValue parse\");\n        assert!(raw >= min, \"{field} below minValue\");\n",
                            host = host_ty,
                            minv = minv,
                            field = field.name
                        ));
                    }
                    if let Some(ref maxv) = field.max_value {
                        code.push_str(&format!(
                            "        let max: {host} = \"{maxv}\".parse().expect(\"maxValue parse\");\n        assert!(raw <= max, \"{field} above maxValue\");\n",
                            host = host_ty,
                            maxv = maxv,
                            field = field.name
                        ));
                    }
                    if field.presence.as_deref() != Some("optional")
                        && let Some(ref nullv) = field.null_value
                    {
                        code.push_str(&format!(
                                "        let null_val: {host} = \"{nullv}\".parse().expect(\"nullValue parse\");\n        assert!(raw != null_val, \"{field} uses nullValue but field is required\");\n",
                                host = host_ty,
                                nullv = nullv,
                                field = field.name
                            ));
                    }
                    code.push_str(&format!(
                        "        let encoded = {expr};\n        write_bytes_at(&mut self.buf, {offset}usize, &encoded);\n        self\n    }}\n",
                        expr = encoded_expr,
                        offset = offset
                    ));
                } else {
                    code.push_str(&format!(
                        "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_at(&mut self.buf, {offset}usize, &encoded);\n        self\n    }}\n",
                        name = field_name,
                        param = param_ty,
                        expr = encoded_expr,
                        offset = offset
                    ));
                }
            } else {
                code.push_str(&format!(
                    "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_at(&mut self.buf, {offset}usize, &encoded);\n        self\n    }}\n",
                    name = field_name,
                    param = param_ty,
                    expr = encoded_expr,
                    offset = offset
                ));
            }
        }
    }
    for g in &groups {
        let builder_ty = format!("{}GroupBuilder", g.name);
        let gname = g.name.to_snake_case();
        code.push_str(&format!(
            "    pub fn {name}<F>(&mut self, f: F) -> &mut Self\n    where\n        F: FnOnce(&mut {builder_ty}),\n    {{\n        let mut builder = {builder_ty}::new(&mut self.buf);\n        f(&mut builder);\n        builder.finish();\n        self\n    }}\n",
            name = gname,
            builder_ty = builder_ty
        ));
    }
    for d in &data_fields {
        let name = d.name.to_snake_case();
        let kind = length_kind_token(length_kind_for_data(&d.ty, schema, opts));
        code.push_str(&format!(
            "    pub fn {name}(&mut self, bytes: &[u8]) -> Result<&mut Self, EncodeError> {{\n        write_var_data(&mut self.buf, bytes, {kind}, ENDIAN)?;\n        Ok(self)\n    }}\n",
            name = name,
            kind = kind
        ));
    }
    code.push_str("    pub fn finish(self) -> Vec<u8> {\n        self.buf\n    }\n");
    code.push_str(
        "    pub fn finish_with_header(self) -> Vec<u8> {\n        let mut out = Vec::with_capacity(self.buf.len() + core::mem::size_of::<MessageHeader>());\n        let header = MessageHeader {\n            block_length: U16::new(Self::BLOCK_LENGTH),\n            template_id: U16::new(Self::TEMPLATE_ID),\n            schema_id: U16::new(Self::SCHEMA_ID),\n            version: U16::new(Self::SCHEMA_VERSION),\n        };\n        out.extend_from_slice(header.as_bytes());\n        out.extend_from_slice(&self.buf);\n        out\n    }\n",
    );
    code.push_str("}\n\n");
    code.push_str(&format!(
        "/// Zero-allocation encoder writing directly into caller-provided memory.\n\
pub struct {}<'a> {{\n    buf: &'a mut [u8],\n    used: usize,\n}}\n\n",
        encoder_name
    ));
    code.push_str(&format!("impl<'a> {}<'a> {{\n", encoder_name));
    code.push_str(&format!(
        "    pub const BLOCK_LENGTH: u16 = {}::BLOCK_LENGTH;\n",
        msg.name
    ));
    code.push_str(&format!(
        "    pub const FIXED_LEN: usize = {};\n",
        fixed_required
    ));
    code.push_str(
        "    /// Create an encoder over `buf`.\n    ///\n    /// The slice must have at least `FIXED_LEN` bytes. The fixed block is zero-initialized,\n    /// and variable-size fields append after the fixed block.\n    pub fn new(buf: &'a mut [u8]) -> Result<Self, EncodeIntoError> {\n        if buf.len() < Self::FIXED_LEN {\n            return Err(EncodeIntoError::BufferTooSmall { required: Self::FIXED_LEN, available: buf.len() });\n        }\n        buf[..Self::FIXED_LEN].fill(0);\n        Ok(Self { buf, used: Self::FIXED_LEN })\n    }\n",
    );
    code.push_str("    pub fn encoded_len(&self) -> usize {\n        self.used\n    }\n");
    code.push_str("    pub fn as_slice(&self) -> &[u8] {\n        &self.buf[..self.used]\n    }\n");
    for layout in &field_layouts {
        let field = layout.field;
        if field_is_constant(field, schema) {
            continue;
        }
        let offset = layout.offset;
        let field_name = field.name.to_snake_case();
        if let Some(resolved_type) =
            resolve_type(&field.ty, schema, opts, field.byte_order.as_deref())
        {
            let param_ty = builder_param_type(&resolved_type);
            let encoded_expr = builder_value_expr("value", &resolved_type);
            let needs_checks = field.min_value.is_some()
                || field.max_value.is_some()
                || (field.presence.as_deref() != Some("optional") && field.null_value.is_some());
            if needs_checks {
                if let (Some(host_ty), Some(raw_expr)) = (
                    host_type_for_field(field, schema),
                    raw_expr_for_value(field, "value", schema, opts),
                ) {
                    code.push_str(&format!("    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let raw: {host} = {raw};\n", name = field_name, param = param_ty, host = host_ty, raw = raw_expr));
                    if let Some(ref minv) = field.min_value {
                        code.push_str(&format!(
                            "        let min: {host} = \"{minv}\".parse().expect(\"minValue parse\");\n        assert!(raw >= min, \"{field} below minValue\");\n",
                            host = host_ty,
                            minv = minv,
                            field = field.name
                        ));
                    }
                    if let Some(ref maxv) = field.max_value {
                        code.push_str(&format!(
                            "        let max: {host} = \"{maxv}\".parse().expect(\"maxValue parse\");\n        assert!(raw <= max, \"{field} above maxValue\");\n",
                            host = host_ty,
                            maxv = maxv,
                            field = field.name
                        ));
                    }
                    if field.presence.as_deref() != Some("optional")
                        && let Some(ref nullv) = field.null_value
                    {
                        code.push_str(&format!(
                                "        let null_val: {host} = \"{nullv}\".parse().expect(\"nullValue parse\");\n        assert!(raw != null_val, \"{field} uses nullValue but field is required\");\n",
                                host = host_ty,
                                nullv = nullv,
                                field = field.name
                            ));
                    }
                    code.push_str(&format!(
                        "        let encoded = {expr};\n        write_bytes_into(self.buf, {offset}usize, &encoded).expect(\"fixed field write in bounds\");\n        self\n    }}\n",
                        expr = encoded_expr,
                        offset = offset
                    ));
                } else {
                    code.push_str(&format!(
                        "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_into(self.buf, {offset}usize, &encoded).expect(\"fixed field write in bounds\");\n        self\n    }}\n",
                        name = field_name,
                        param = param_ty,
                        expr = encoded_expr,
                        offset = offset
                    ));
                }
            } else {
                code.push_str(&format!(
                    "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_into(self.buf, {offset}usize, &encoded).expect(\"fixed field write in bounds\");\n        self\n    }}\n",
                    name = field_name,
                    param = param_ty,
                    expr = encoded_expr,
                    offset = offset
                ));
            }
        }
    }
    for d in &data_fields {
        let name = d.name.to_snake_case();
        let kind = length_kind_token(length_kind_for_data(&d.ty, schema, opts));
        code.push_str(&format!(
            "    pub fn {name}(&mut self, bytes: &[u8]) -> Result<&mut Self, EncodeIntoError> {{\n        let written = write_var_data_into(self.buf, self.used, bytes, {kind}, ENDIAN)?;\n        self.used = self.used.checked_add(written).ok_or(EncodeIntoError::InvalidState(\"encoded length overflow\"))?;\n        Ok(self)\n    }}\n",
            name = name,
            kind = kind
        ));
    }
    for g in &groups {
        let encoder_ty = format!("{}GroupEncoder", g.name);
        let gname = g.name.to_snake_case();
        code.push_str(&format!(
            "    pub fn {name}<F>(&mut self, f: F) -> Result<&mut Self, EncodeIntoError>\n    where\n        F: FnOnce(&mut {encoder}<'_>) -> Result<(), EncodeIntoError>,\n    {{\n        let mut encoder = {encoder}::new(self.buf, self.used)?;\n        f(&mut encoder)?;\n        self.used = encoder.finish()?;\n        Ok(self)\n    }}\n",
            name = gname,
            encoder = encoder_ty
        ));
    }
    code.push_str("    pub fn finish(self) -> usize {\n        self.used\n    }\n");
    code.push_str("}\n\n");
    code.push_str(&format!("impl {} {{\n", msg.name));
    code.push_str(&format!(
        "    /// Encode message body (fixed block + variable data) into `dst` without allocation.\n    ///\n    /// Returns number of body bytes written.\n    pub fn encode_body_into<F>(dst: &mut [u8], f: F) -> Result<usize, EncodeIntoError>\n    where\n        F: FnOnce(&mut {encoder}<'_>) -> Result<(), EncodeIntoError>,\n    {{\n        let mut encoder = {encoder}::new(dst)?;\n        f(&mut encoder)?;\n        Ok(encoder.finish())\n    }}\n",
        encoder = encoder_name
    ));
    code.push_str(&format!(
        "    /// Encode header + body into `dst` without allocation.\n    ///\n    /// `dst` must fit `size_of::<MessageHeader>() + body_len`. Returns total bytes written.\n    pub fn encode_with_header_into<F>(dst: &mut [u8], header: MessageHeader, f: F) -> Result<usize, EncodeIntoError>\n    where\n        F: FnOnce(&mut {encoder}<'_>) -> Result<(), EncodeIntoError>,\n    {{\n        let header_len = core::mem::size_of::<MessageHeader>();\n        if dst.len() < header_len {{\n            return Err(EncodeIntoError::BufferTooSmall {{ required: header_len, available: dst.len() }});\n        }}\n        let body_len = {{\n            let (_, body_dst) = dst.split_at_mut(header_len);\n            let mut encoder = {encoder}::new(body_dst)?;\n            f(&mut encoder)?;\n            encoder.finish()\n        }};\n        let total = header_len.checked_add(body_len).ok_or(EncodeIntoError::InvalidState(\"encoded frame length overflow\"))?;\n        if dst.len() < total {{\n            return Err(EncodeIntoError::BufferTooSmall {{ required: total, available: dst.len() }});\n        }}\n        write_bytes_into(dst, 0, &header)?;\n        Ok(total)\n    }}\n",
        encoder = encoder_name
    ));
    code.push_str("}\n\n");
    // group helpers (supports nesting)
    for g in groups {
        emit_group(g, schema, opts, &mut code);
    }
    // acting version aware view
    code.push_str(&format!(
        "#[derive(Debug, Clone)]\npub enum {}Body<'a> {{\n    Borrowed(&'a {}, &'a [u8]),\n    Owned(Vec<u8>),\n}}\n\n",
        msg.name, msg.name
    ));
    code.push_str(&format!(
        "impl<'a> core::ops::Deref for {}Body<'a> {{\n    type Target = {};\n    fn deref(&self) -> &Self::Target {{\n        match self {{\n            Self::Borrowed(m, _) => m,\n            Self::Owned(bytes) => {{\n                let (msg, _) = Ref::<_, {}>::from_prefix(bytes.as_slice()).expect(\"padded message\");\n                Ref::into_ref(msg)\n            }}\n        }}\n    }}\n}}\n\n",
        msg.name, msg.name, msg.name
    ));
    code.push_str(&format!(
        "impl<'a> {}Body<'a> {{\n    fn bytes(&self) -> &[u8] {{\n        match self {{\n            Self::Borrowed(_, raw) => raw,\n            Self::Owned(bytes) => bytes.as_slice(),\n        }}\n    }}\n}}\n\n",
        msg.name
    ));
    code.push_str(&format!(
        "#[derive(Debug, Clone)]\npub struct {}View<'a> {{\n    pub body: {}Body<'a>,\n    pub acting_block_length: usize,\n    pub acting_version: u16,\n}}\n\n",
        msg.name, msg.name
    ));
    code.push_str(&format!("impl<'a> {}View<'a> {{\n", msg.name));
    for layout in &field_layouts {
        let field = layout.field;
        let fname = field.name.to_snake_case();
        let since = field.since_version.unwrap_or(0);
        code.push_str(&format!(
            "    pub fn has_{fname}(&self) -> bool {{\n        if self.acting_version < {since} as u16 {{ return false; }}\n        {offset} + {size} <= self.acting_block_length\n    }}\n",
            fname = fname,
            since = since,
            offset = layout.offset,
            size = layout.size
        ));
        if let Some(resolved) = resolve_type(&field.ty, schema, opts, field.byte_order.as_deref()) {
            code.push_str(&format!(
                "    pub fn {fname}(&self) -> Option<&{ty}> {{\n        if !self.has_{fname}() {{ return None; }}\n        let bytes = &self.body.bytes()[{offset}..{offset_plus}];\n        let (r, _) = Ref::<_, {ty}>::from_prefix(bytes).ok()?;\n        Some(Ref::into_ref(r))\n    }}\n",
                fname = fname,
                ty = resolved,
                offset = layout.offset,
                offset_plus = layout.offset + layout.size
            ));
        }
    }
    if !data_fields.is_empty() {
        for d in &data_fields {
            let fn_name = d.name.to_snake_case();
            let kind = length_kind_token(length_kind_for_data(&d.ty, schema, opts));
            code.push_str(&format!(
                "    pub fn parse_{name}<'b>(&self, buf: &'b [u8]) -> Option<(VarData<'b>, &'b [u8])> {{\n        parse_var_data(buf, {kind})\n    }}\n",
                name = fn_name,
                kind = kind
            ));
        }
    }
    code.push_str("}\n\n");
    code.push_str(&format!(
        "pub fn parse_with_header<'a>(body: &'a [u8], header: &MessageHeader) -> Option<({name}View<'a>, &'a [u8])> {{\n    let mut acting_block_length = header.block_length.get() as usize;\n    if acting_block_length == 0 {{ acting_block_length = {name}::BLOCK_LENGTH as usize; }}\n    let acting_version = header.version.get();\n    if body.len() < acting_block_length {{ return None; }}\n    let needed = core::mem::size_of::<{name}>();\n    let (prefix, rest) = body.split_at(acting_block_length);\n    if acting_block_length >= needed {{\n        let (msg, _) = Ref::<_, {name}>::from_prefix(&prefix[..needed]).ok()?;\n        let view = {name}View {{ body: {name}Body::Borrowed(Ref::into_ref(msg), &prefix[..needed]), acting_block_length, acting_version }};\n        Some((view, rest))\n    }} else {{\n        let mut owned = vec![0u8; needed];\n        owned[..acting_block_length].copy_from_slice(prefix);\n        let view = {name}View {{ body: {name}Body::Owned(owned), acting_block_length, acting_version }};\n        Some((view, rest))\n    }}\n}}\n",
        name = msg.name
    ));
    code
}

/// Map an SBE primitive to a Rust zero‑copy type.  Returns `None` if
/// the primitive is not recognised.
fn primitive_to_rust(prim: &str, _opts: &GeneratorOptions) -> Option<String> {
    match prim {
        // boolean represented as u8
        "boolean" | "uint8" => Some("u8".into()),
        "int8" => Some("i8".into()),
        "char" => Some("u8".into()),
        "uint16" => Some("U16".into()),
        "int16" => Some("I16".into()),
        "uint32" => Some("U32".into()),
        "int32" => Some("I32".into()),
        "uint64" => Some("U64".into()),
        "int64" => Some("I64".into()),
        "float" => Some("F32".into()),
        "double" => Some("F64".into()),
        "varStringEncoding" | "varAsciiEncoding" | "varDataEncoding" => None,
        _ => None,
    }
}

fn primitive_to_rust_endian(
    prim: &str,
    schema_endian: &str,
    override_endian: Option<&str>,
) -> Option<String> {
    let endian = override_endian.unwrap_or(schema_endian);
    let short = override_endian.is_none() || override_endian == Some(schema_endian);
    let ty = match prim {
        "boolean" | "uint8" => "u8",
        "int8" => "i8",
        "char" => "u8",
        "uint16" => "U16",
        "int16" => "I16",
        "uint32" => "U32",
        "int32" => "I32",
        "uint64" => "U64",
        "int64" => "I64",
        "float" => "F32",
        "double" => "F64",
        "varStringEncoding" | "varAsciiEncoding" | "varDataEncoding" => return None,
        _ => return None,
    };
    if short {
        Some(ty.into())
    } else {
        Some(format!("zerocopy::byteorder::{}_endian::{}", endian, ty))
    }
}

/// Provide a default constant value for a primitive type.
fn primitive_default_value(prim: &str) -> &'static str {
    match prim {
        "int8" | "uint8" | "char" | "boolean" => "0",
        "int16" | "uint16" => "0",
        "int32" | "uint32" => "0",
        "int64" | "uint64" => "0",
        "float" => "0.0",
        "double" => "0.0",
        _ => "0",
    }
}

fn const_scalar_expr(prim: &str, rust_type: &str, raw: &str) -> Option<String> {
    let trimmed = raw.trim();
    let value = match prim {
        "char" => {
            let byte = trimmed.as_bytes().first().copied().unwrap_or(0);
            format!("{}u8", byte)
        }
        "boolean" => match trimmed {
            "true" => "1u8".into(),
            "false" => "0u8".into(),
            other => other.to_string(),
        },
        _ => trimmed.to_string(),
    };
    let base = rust_type.rsplit("::").next().unwrap_or(rust_type);
    Some(match base {
        "U16" | "I16" | "U32" | "I32" | "U64" | "I64" | "F32" | "F64" => {
            format!("{}::new({})", rust_type, value)
        }
        _ => value,
    })
}

fn const_array_expr(
    prim: &str,
    rust_type: &str,
    raw: &str,
    len: usize,
) -> Option<(String, String)> {
    if len == 0 {
        return None;
    }
    if prim == "char" {
        let mut bytes = raw.as_bytes().to_vec();
        while bytes.len() < len {
            bytes.push(0u8);
        }
        if bytes.len() > len {
            bytes.truncate(len);
        }
        let expr = format!(
            "[{}]",
            bytes
                .iter()
                .map(|b| format!("{}u8", b))
                .collect::<Vec<_>>()
                .join(", ")
        );
        let ty = format!("[{}; {}]", rust_type, len);
        return Some((ty, expr));
    }
    let mut parts: Vec<&str> = raw
        .split(|c: char| c.is_ascii_whitespace() || c == ',')
        .filter(|s| !s.is_empty())
        .collect();
    if parts.is_empty() {
        parts.push(primitive_default_value(prim));
    }
    let mut exprs = Vec::new();
    for part in &parts {
        exprs.push(const_scalar_expr(prim, rust_type, part)?);
    }
    if exprs.len() < len {
        let fill = exprs.last().cloned()?;
        exprs.resize(len, fill);
    } else if exprs.len() > len {
        exprs.truncate(len);
    }
    let expr = format!("[{}]", exprs.join(", "));
    let ty = format!("[{}; {}]", rust_type, len);
    Some((ty, expr))
}

/// Resolve a field's type name to the appropriate Rust type.  This
/// consults the type map for user defined types or falls back to
/// primitive mapping.  Unsupported types return `None`, causing the
/// field to be skipped.
fn resolve_type(
    name: &str,
    schema: &Schema,
    opts: &GeneratorOptions,
    override_endian: Option<&str>,
) -> Option<String> {
    // first check if it's a primitive built‑in
    if let Some(rust) = primitive_to_rust_endian(name, &opts.endian, override_endian) {
        return Some(rust);
    }
    // check user defined types
    if let Some(td) = schema.types.get(name) {
        match td {
            TypeDef::Primitive {
                name: tname,
                primitive,
                length,
                ..
            } => {
                let rust = primitive_to_rust_endian(primitive, &opts.endian, override_endian)?;
                if let Some(len) = length {
                    Some(format!("[{}; {}]", rust, len))
                } else if override_endian.is_some() && override_endian != Some(opts.endian.as_str())
                {
                    Some(rust)
                } else {
                    Some(tname.clone())
                }
            }
            TypeDef::Enum { name: tname, .. } => Some(tname.clone()),
            TypeDef::Set { name: tname, .. } => Some(tname.clone()),
            TypeDef::Composite { name: tname, .. } => Some(tname.clone()),
        }
    } else {
        None
    }
}

#[allow(dead_code)]
fn resolved_size_bytes(rust_type: &str) -> Option<usize> {
    let ty = rust_type.trim();
    if let Some(rest) = ty.strip_prefix('[')
        && let Some((inner, len_part)) = rest.split_once(';')
    {
        let inner = inner.trim();
        let len_part = len_part.trim().trim_end_matches(']');
        if let Ok(n) = len_part.parse::<usize>()
            && let Some(sz) = resolved_size_bytes(inner)
        {
            return Some(sz * n);
        }
    }
    let base = ty.rsplit("::").next().unwrap_or(ty);
    match base {
        "u8" | "i8" | "char" => Some(1),
        "U16" | "I16" | "u16" | "i16" => Some(2),
        "U32" | "I32" | "u32" | "i32" | "F32" | "f32" => Some(4),
        "U64" | "I64" | "u64" | "i64" | "F64" | "f64" => Some(8),
        _ => None,
    }
}

fn primitive_size_bytes(prim: &str) -> Option<usize> {
    match prim {
        "boolean" | "uint8" | "int8" | "char" => Some(1),
        "uint16" | "int16" => Some(2),
        "uint32" | "int32" | "float" => Some(4),
        "uint64" | "int64" | "double" => Some(8),
        _ => None,
    }
}

fn type_size_bytes(
    ty: &str,
    schema: &Schema,
    _opts: &GeneratorOptions,
    depth: usize,
    respect_constant: bool,
) -> Option<usize> {
    if depth > 8 {
        return None;
    }
    if let Some(sz) = primitive_size_bytes(ty) {
        return Some(sz);
    }
    if let Some(td) = schema.types.get(ty) {
        match td {
            TypeDef::Primitive {
                primitive,
                length,
                presence,
                ..
            } => {
                if respect_constant && presence.as_deref() == Some("constant") {
                    return Some(0);
                }
                let base = primitive_size_bytes(primitive)?;
                Some(base * length.unwrap_or(1))
            }
            TypeDef::Enum { encoding, .. } | TypeDef::Set { encoding, .. } => {
                primitive_size_bytes(encoding)
            }
            TypeDef::Composite { fields, .. } => {
                let mut total = 0usize;
                for f in fields {
                    match f {
                        CompositeField::Type {
                            primitive,
                            length,
                            presence,
                            ..
                        } => {
                            if presence.as_deref() == Some("constant") {
                                continue;
                            }
                            let base = primitive_size_bytes(primitive)?;
                            total += base * length.unwrap_or(1);
                        }
                        CompositeField::Ref { ty, .. } => {
                            total +=
                                type_size_bytes(ty, schema, _opts, depth + 1, respect_constant)?;
                        }
                    }
                }
                Some(total)
            }
        }
    } else {
        None
    }
}

fn field_size_bytes(field: &Field, schema: &Schema, opts: &GeneratorOptions) -> Option<usize> {
    // constant fields are not encoded
    if field_is_constant(field, schema) {
        return Some(0);
    }
    if field.presence.is_some()
        && let Some(TypeDef::Primitive {
            primitive,
            length,
            presence: Some(p),
            ..
        }) = schema.types.get(&field.ty)
        && p == "constant"
    {
        let base = primitive_size_bytes(primitive)?;
        return Some(base * length.unwrap_or(1));
    }
    type_size_bytes(&field.ty, schema, opts, 0, true)
}

fn write_fields_with_offsets(
    code: &mut String,
    fields: &[&Field],
    schema: &Schema,
    opts: &GeneratorOptions,
) {
    let mut cur_offset: Option<usize> = Some(0);
    let mut pad_idx = 0;
    for field in fields {
        if field_is_constant(field, schema) {
            continue;
        }
        let ty_name = &field.ty;
        let field_name = field.name.to_snake_case();
        if let Some(rust_type) = resolve_type(ty_name, schema, opts, field.byte_order.as_deref()) {
            let field_sz = field_size_bytes(field, schema, opts);
            if let (Some(target), Some(cur)) = (field.offset.map(|o| o as usize), cur_offset)
                && target > cur
            {
                let pad = target - cur;
                code.push_str(&format!("    __padding{}: [u8; {}],\n", pad_idx, pad));
                pad_idx += 1;
                cur_offset = Some(target);
            }
            maybe_doc_comment(code, &field.description);
            code.push_str(&format!("    pub {}: {},\n", field_name, rust_type));
            if let Some(sz) = field_sz {
                if let Some(cur) = cur_offset {
                    cur_offset = Some(cur + sz);
                }
            } else {
                cur_offset = None;
            }
        }
    }
}

#[derive(Clone, Copy)]
enum LengthKind {
    U8,
    U16,
    U32,
    U64,
}

fn primitive_length_kind(prim: &str) -> Option<LengthKind> {
    match prim {
        "boolean" | "uint8" | "int8" | "char" => Some(LengthKind::U8),
        "uint16" | "int16" => Some(LengthKind::U16),
        "uint32" | "int32" => Some(LengthKind::U32),
        "uint64" | "int64" => Some(LengthKind::U64),
        _ => None,
    }
}

fn length_kind_for_data(ty: &str, schema: &Schema, opts: &GeneratorOptions) -> LengthKind {
    if let Some(kind) = primitive_length_kind(ty) {
        return kind;
    }
    if let Some(td) = schema.types.get(ty) {
        match td {
            TypeDef::Primitive { primitive, .. } => {
                if let Some(kind) = primitive_length_kind(primitive) {
                    return kind;
                }
            }
            TypeDef::Enum { encoding, .. } | TypeDef::Set { encoding, .. } => {
                if let Some(kind) = primitive_length_kind(encoding) {
                    return kind;
                }
            }
            TypeDef::Composite { fields, .. } => {
                for f in fields {
                    if let CompositeField::Type {
                        primitive,
                        presence,
                        ..
                    } = f
                    {
                        if presence.as_deref() == Some("constant") {
                            continue;
                        }
                        if let Some(kind) = primitive_length_kind(primitive) {
                            return kind;
                        }
                    }
                }
            }
        }
    }
    // fall back to little length prefix if unknown
    let _ = opts;
    LengthKind::U8
}

fn length_kind_token(kind: LengthKind) -> &'static str {
    match kind {
        LengthKind::U8 => "LengthKind::U8",
        LengthKind::U16 => "LengthKind::U16",
        LengthKind::U32 => "LengthKind::U32",
        LengthKind::U64 => "LengthKind::U64",
    }
}

fn emit_group(g: &Group, schema: &Schema, opts: &GeneratorOptions, code: &mut String) {
    let group_name = &g.name;
    let group_snake = g.name.to_snake_case();
    let entry_struct = format!("{}Entry", group_name);
    let entry_view = format!("{}EntryView", group_name);
    let group_struct = format!("{}Group", group_name);
    let g_block_length = group_block_length(g, schema, opts);
    let iter_name = format!("{}Iter", group_name);
    let group_builder = format!("{}GroupBuilder", group_name);
    let entry_builder = format!("{}EntryBuilder", group_name);
    let group_encoder = format!("{}GroupEncoder", group_name);
    let entry_encoder = format!("{}EntryEncoder", group_name);
    let g_fields = group_fields(g);
    let entry_layout = layout_fields(&g_fields, schema, opts);
    let entry_fixed_required = g_block_length.max(
        entry_layout
            .iter()
            .map(|layout| layout.offset + layout.size)
            .max()
            .unwrap_or(0),
    );
    let g_data = group_data(g);
    let g_nested = group_groups(g);
    let has_data = !g_data.is_empty();
    let has_nested = !g_nested.is_empty();
    let has_any_var = has_data || has_nested;
    let dim = dimension_fields(schema, &g.dimension_type, opts);
    let block_len_expr = if let Some(bl) = g.block_length {
        bl.to_string()
    } else {
        format!("core::mem::size_of::<{}>()", entry_struct)
    };
    let dim_block_offset =
        composite_field_offset(&g.dimension_type, &dim.block_field, schema, opts).unwrap_or(0);
    let dim_count_offset =
        composite_field_offset(&g.dimension_type, &dim.count_field, schema, opts).unwrap_or(0);
    let dim_size = type_size_bytes(&g.dimension_type, schema, opts, 0, true).unwrap_or(0);

    code.push_str(&format!(
        "pub fn parse_{}<'a>(buf: &'a [u8]) -> Option<{}<'a>> {{\n",
        group_snake, group_struct
    ));
    code.push_str(&format!(
        "    let (header, payload) = {}::parse_prefix(buf)?;\n",
        g.dimension_type
    ));
    code.push_str(&format!(
        "    let header_block_len = {};\n",
        to_usize_expr(&format!("header.{}", dim.block_field), &dim.block_field_ty)
    ));
    code.push_str(&format!(
        "    let declared = {} as usize;\n",
        block_len_expr
    ));
    code.push_str(
        "    let block_length = if header_block_len == 0 { declared } else { header_block_len };\n",
    );
    code.push_str(&format!(
        "    Some({} {{ header, payload, block_length }})\n",
        group_struct
    ));
    code.push_str("}\n\n");

    code.push_str(&format!(
        "#[derive(Debug, Clone, Copy)]\npub struct {}<'a> {{\n    pub header: &'a {},\n    payload: &'a [u8],\n    block_length: usize,\n}}\n\n",
        group_struct, g.dimension_type
    ));
    code.push_str(&format!("impl<'a> {}<'a> {{\n", group_struct));
    let g_since = g.since_version.unwrap_or(0);
    let g_sem = g.semantic_type.clone().unwrap_or_default();
    code.push_str(&format!(
        "    pub const SINCE_VERSION: u32 = {};\n",
        g_since
    ));
    code.push_str(&format!(
        "    pub const SEMANTIC_TYPE: &'static str = \"{}\";\n",
        g_sem
    ));
    code.push_str(&format!(
        "    pub fn count(&self) -> usize {{ {} }}\n",
        to_usize_expr(
            &format!("self.header.{}", dim.count_field),
            &dim.count_field_ty
        )
    ));
    code.push_str(&format!("    pub fn iter(&self) -> {}<'a> {{\n", iter_name));
    if has_data {
        code.push_str(&format!(
            "        static DATA_KINDS: [LengthKind; {}] = [{}];\n",
            g_data.len(),
            g_data
                .iter()
                .map(|d| length_kind_token(length_kind_for_data(&d.ty, schema, opts)))
                .collect::<Vec<_>>()
                .join(", ")
        ));
        code.push_str(&format!(
            "        {} {{ remaining: self.payload, entries_left: self.count(), block_length: self.block_length, data_kinds: &DATA_KINDS }}\n",
            iter_name
        ));
    } else {
        code.push_str(&format!(
            "        {} {{ remaining: self.payload, entries_left: self.count(), block_length: self.block_length }}\n",
            iter_name
        ));
    }
    code.push_str("    }\n}\n\n");

    if has_data {
        code.push_str(&format!(
            "pub struct {}<'a> {{\n    remaining: &'a [u8],\n    entries_left: usize,\n    block_length: usize,\n    data_kinds: &'static [LengthKind],\n}}\n\n",
            iter_name
        ));
    } else {
        code.push_str(&format!(
            "pub struct {}<'a> {{\n    remaining: &'a [u8],\n    entries_left: usize,\n    block_length: usize,\n}}\n\n",
            iter_name
        ));
    }
    code.push_str(&format!(
        "impl<'a> {}<'a> {{\n    pub fn remainder(&self) -> &'a [u8] {{ self.remaining }}\n}}\n\n",
        iter_name
    ));

    code.push_str(&format!(
        "#[derive(Debug, Clone, Copy)]\npub struct {}<'a> {{\n    pub body: &'a {},\n",
        entry_view, entry_struct
    ));
    for nested in &g_nested {
        code.push_str(&format!(
            "    pub {}: {}Group<'a>,\n",
            nested.name.to_snake_case(),
            nested.name
        ));
    }
    for d in &g_data {
        code.push_str(&format!(
            "    pub {}: VarData<'a>,\n",
            d.name.to_snake_case()
        ));
    }
    code.push_str("}\n\n");

    code.push_str("#[repr(C)]\n");
    code.push_str(
        "#[derive(Debug, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy)]\n",
    );
    if let Some(desc) = &g.description {
        let doc = format!("Group: {}", desc);
        push_doc_comment(code, &doc);
    }
    code.push_str(&format!("pub struct {} {{\n", entry_struct));
    write_fields_with_offsets(code, &g_fields, schema, opts);
    code.push_str("}\n\n");
    code.push_str(&format!("impl {} {{\n", entry_struct));
    code.push_str(PARSE_PREFIX_METHOD);
    code.push_str("}\n\n");
    push_constant_field_impl(code, &entry_struct, &g_fields, schema, opts);
    if !g_fields.is_empty() {
        code.push_str(&format!("impl {} {{\n", entry_struct));
        optional_methods_for_fields(code, &g_fields, schema, opts, "self");
        for f in &g_fields {
            if let Some(off) = f.offset {
                code.push_str(&format!(
                    "    pub const {}_OFFSET: u32 = {};\n",
                    f.name.to_uppercase(),
                    off
                ));
            }
            let sv = f.since_version.unwrap_or(0);
            let sem = f.semantic_type.clone().unwrap_or_default();
            code.push_str(&format!(
                "    pub const {}_SINCE_VERSION: u32 = {};\n",
                f.name.to_uppercase(),
                sv
            ));
            code.push_str(&format!(
                "    pub const {}_SEMANTIC_TYPE: &'static str = \"{}\";\n",
                f.name.to_uppercase(),
                sem
            ));
        }
        code.push_str("}\n\n");
    }
    code.push_str(&format!(
        "pub struct {}<'a> {{\n    buf: &'a mut Vec<u8>,\n    header_start: usize,\n    count: usize,\n}}\n\n",
        group_builder
    ));
    code.push_str(&format!(
        "pub struct {}<'a> {{\n    buf: &'a mut Vec<u8>,\n    start: usize,\n}}\n\n",
        entry_builder
    ));
    code.push_str(&format!("impl<'a> {}<'a> {{\n", group_builder));
    code.push_str(&format!(
        "    pub const BLOCK_LENGTH: u16 = {};\n",
        g_block_length
    ));
    code.push_str(&format!(
        "    pub const HEADER_SIZE: usize = {};\n",
        dim_size
    ));
    let block_expr = builder_value_expr("Self::BLOCK_LENGTH", &dim.block_field_ty);
    let count_expr = builder_value_expr("0", &dim.count_field_ty);
    code.push_str(&format!(
        "    pub fn new(buf: &'a mut Vec<u8>) -> Self {{\n        let header_start = buf.len();\n        buf.resize(header_start + Self::HEADER_SIZE, 0);\n        let block_len = {block_expr};\n        write_bytes_at(buf, header_start + {block_off}, &block_len);\n        let count = {count_expr};\n        write_bytes_at(buf, header_start + {count_off}, &count);\n        Self {{ buf, header_start, count: 0 }}\n    }}\n",
        block_expr = block_expr,
        block_off = dim_block_offset,
        count_expr = count_expr,
        count_off = dim_count_offset
    ));
    code.push_str(&format!(
        "    pub fn entry<F>(&mut self, f: F) -> &mut Self\n    where\n        F: FnOnce(&mut {entry_builder}),\n    {{\n        let start = self.buf.len();\n        self.buf.resize(start + Self::BLOCK_LENGTH as usize, 0);\n        let mut entry = {entry_builder} {{ buf: self.buf, start }};\n        f(&mut entry);\n        self.count += 1;\n        self\n    }}\n",
        entry_builder = entry_builder
    ));
    let count_finish_expr = count_value_expr("self.count", &dim.count_field_ty);
    code.push_str(&format!(
        "    pub fn finish(self) {{\n        let count = {count_expr};\n        write_bytes_at(self.buf, self.header_start + {count_off}, &count);\n    }}\n",
        count_expr = count_finish_expr,
        count_off = dim_count_offset
    ));
    code.push_str("}\n\n");
    code.push_str(&format!("impl<'a> {}<'a> {{\n", entry_builder));
    for layout in &entry_layout {
        let field = layout.field;
        if field_is_constant(field, schema) {
            continue;
        }
        let offset = layout.offset;
        if let Some(resolved_type) =
            resolve_type(&field.ty, schema, opts, field.byte_order.as_deref())
        {
            let param_ty = builder_param_type(&resolved_type);
            let encoded_expr = builder_value_expr("value", &resolved_type);
            let needs_checks = field.min_value.is_some()
                || field.max_value.is_some()
                || (field.presence.as_deref() != Some("optional") && field.null_value.is_some());
            if needs_checks {
                if let (Some(host_ty), Some(raw_expr)) = (
                    host_type_for_field(field, schema),
                    raw_expr_for_value(field, "value", schema, opts),
                ) {
                    code.push_str(&format!("    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let raw: {host} = {raw};\n", name = field.name.to_snake_case(), param = param_ty, host = host_ty, raw = raw_expr));
                    if let Some(ref minv) = field.min_value {
                        code.push_str(&format!(
                            "        let min: {host} = \"{minv}\".parse().expect(\"minValue parse\");\n        assert!(raw >= min, \"{field} below minValue\");\n",
                            host = host_ty,
                            minv = minv,
                            field = field.name
                        ));
                    }
                    if let Some(ref maxv) = field.max_value {
                        code.push_str(&format!(
                            "        let max: {host} = \"{maxv}\".parse().expect(\"maxValue parse\");\n        assert!(raw <= max, \"{field} above maxValue\");\n",
                            host = host_ty,
                            maxv = maxv,
                            field = field.name
                        ));
                    }
                    if field.presence.as_deref() != Some("optional")
                        && let Some(ref nullv) = field.null_value
                    {
                        code.push_str(&format!(
                                "        let null_val: {host} = \"{nullv}\".parse().expect(\"nullValue parse\");\n        assert!(raw != null_val, \"{field} uses nullValue but field is required\");\n",
                                host = host_ty,
                                nullv = nullv,
                                field = field.name
                            ));
                    }
                    code.push_str(&format!(
                        "        let encoded = {expr};\n        write_bytes_at(self.buf, self.start + {offset}usize, &encoded);\n        self\n    }}\n",
                        expr = encoded_expr,
                        offset = offset
                    ));
                } else {
                    code.push_str(&format!(
                        "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_at(self.buf, self.start + {offset}usize, &encoded);\n        self\n    }}\n",
                        name = field.name.to_snake_case(),
                        param = param_ty,
                        expr = encoded_expr,
                        offset = offset
                    ));
                }
            } else {
                code.push_str(&format!(
                    "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_at(self.buf, self.start + {offset}usize, &encoded);\n        self\n    }}\n",
                    name = field.name.to_snake_case(),
                    param = param_ty,
                    expr = encoded_expr,
                    offset = offset
                ));
            }
        }
    }
    for nested in &g_nested {
        let nested_builder = format!("{}GroupBuilder", nested.name);
        let name = nested.name.to_snake_case();
        code.push_str(&format!(
            "    pub fn {name}<F>(&mut self, f: F) -> &mut Self\n    where\n        F: FnOnce(&mut {builder}),\n    {{\n        let mut builder = {builder}::new(self.buf);\n        f(&mut builder);\n        builder.finish();\n        self\n    }}\n",
            name = name,
            builder = nested_builder
        ));
    }
    for d in &g_data {
        let name = d.name.to_snake_case();
        let kind = length_kind_token(length_kind_for_data(&d.ty, schema, opts));
        code.push_str(&format!(
            "    pub fn {name}(&mut self, bytes: &[u8]) -> Result<&mut Self, EncodeError> {{\n        write_var_data(self.buf, bytes, {kind}, ENDIAN)?;\n        Ok(self)\n    }}\n",
            name = name,
            kind = kind
        ));
    }
    code.push_str("}\n\n");
    code.push_str(&format!(
        "pub struct {}<'a> {{\n    buf: &'a mut [u8],\n    cursor: usize,\n    header_start: usize,\n    count: usize,\n}}\n\n",
        group_encoder
    ));
    code.push_str(&format!(
        "pub struct {}<'a> {{\n    buf: &'a mut [u8],\n    cursor: usize,\n    start: usize,\n}}\n\n",
        entry_encoder
    ));
    code.push_str(&format!("impl<'a> {}<'a> {{\n", group_encoder));
    code.push_str(&format!(
        "    pub const BLOCK_LENGTH: u16 = {};\n",
        g_block_length
    ));
    code.push_str(&format!(
        "    pub const HEADER_SIZE: usize = {};\n",
        dim_size
    ));
    code.push_str(&format!(
        "    pub const ENTRY_FIXED_LEN: usize = {};\n",
        entry_fixed_required
    ));
    code.push_str(&format!(
        "    pub fn new(buf: &'a mut [u8], cursor: usize) -> Result<Self, EncodeIntoError> {{\n        let header_end = cursor.checked_add(Self::HEADER_SIZE).ok_or(EncodeIntoError::InvalidState(\"group header offset overflow\"))?;\n        if header_end > buf.len() {{\n            return Err(EncodeIntoError::BufferTooSmall {{ required: header_end, available: buf.len() }});\n        }}\n        if header_end > cursor {{\n            buf[cursor..header_end].fill(0);\n        }}\n        let block_len = {block_expr};\n        write_bytes_into(buf, cursor + {block_off}, &block_len)?;\n        let count = {count_expr};\n        write_bytes_into(buf, cursor + {count_off}, &count)?;\n        Ok(Self {{ buf, cursor: header_end, header_start: cursor, count: 0 }})\n    }}\n",
        block_expr = block_expr,
        block_off = dim_block_offset,
        count_expr = count_expr,
        count_off = dim_count_offset
    ));
    code.push_str(&format!(
        "    pub fn entry<F>(&mut self, f: F) -> Result<&mut Self, EncodeIntoError>\n    where\n        F: FnOnce(&mut {entry_encoder}<'_>) -> Result<(), EncodeIntoError>,\n    {{\n        let mut entry = {entry_encoder}::new(self.buf, self.cursor)?;\n        f(&mut entry)?;\n        self.cursor = entry.finish();\n        self.count = self.count.checked_add(1).ok_or(EncodeIntoError::InvalidState(\"group entry count overflow\"))?;\n        Ok(self)\n    }}\n",
        entry_encoder = entry_encoder
    ));
    code.push_str(&format!(
        "    pub fn finish(self) -> Result<usize, EncodeIntoError> {{\n        let count = {count_expr};\n        write_bytes_into(self.buf, self.header_start + {count_off}, &count)?;\n        Ok(self.cursor)\n    }}\n",
        count_expr = count_finish_expr,
        count_off = dim_count_offset
    ));
    code.push_str("}\n\n");
    code.push_str(&format!("impl<'a> {}<'a> {{\n", entry_encoder));
    code.push_str(&format!(
        "    pub fn new(buf: &'a mut [u8], cursor: usize) -> Result<Self, EncodeIntoError> {{\n        let start = cursor;\n        let end = start.checked_add({entry_fixed_len}).ok_or(EncodeIntoError::InvalidState(\"group entry offset overflow\"))?;\n        if end > buf.len() {{\n            return Err(EncodeIntoError::BufferTooSmall {{ required: end, available: buf.len() }});\n        }}\n        if end > start {{\n            buf[start..end].fill(0);\n        }}\n        Ok(Self {{ buf, cursor: end, start }})\n    }}\n",
        entry_fixed_len = entry_fixed_required
    ));
    for layout in &entry_layout {
        let field = layout.field;
        if field_is_constant(field, schema) {
            continue;
        }
        let offset = layout.offset;
        if let Some(resolved_type) =
            resolve_type(&field.ty, schema, opts, field.byte_order.as_deref())
        {
            let param_ty = builder_param_type(&resolved_type);
            let encoded_expr = builder_value_expr("value", &resolved_type);
            let needs_checks = field.min_value.is_some()
                || field.max_value.is_some()
                || (field.presence.as_deref() != Some("optional") && field.null_value.is_some());
            if needs_checks {
                if let (Some(host_ty), Some(raw_expr)) = (
                    host_type_for_field(field, schema),
                    raw_expr_for_value(field, "value", schema, opts),
                ) {
                    code.push_str(&format!("    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let raw: {host} = {raw};\n", name = field.name.to_snake_case(), param = param_ty, host = host_ty, raw = raw_expr));
                    if let Some(ref minv) = field.min_value {
                        code.push_str(&format!(
                            "        let min: {host} = \"{minv}\".parse().expect(\"minValue parse\");\n        assert!(raw >= min, \"{field} below minValue\");\n",
                            host = host_ty,
                            minv = minv,
                            field = field.name
                        ));
                    }
                    if let Some(ref maxv) = field.max_value {
                        code.push_str(&format!(
                            "        let max: {host} = \"{maxv}\".parse().expect(\"maxValue parse\");\n        assert!(raw <= max, \"{field} above maxValue\");\n",
                            host = host_ty,
                            maxv = maxv,
                            field = field.name
                        ));
                    }
                    if field.presence.as_deref() != Some("optional")
                        && let Some(ref nullv) = field.null_value
                    {
                        code.push_str(&format!(
                                "        let null_val: {host} = \"{nullv}\".parse().expect(\"nullValue parse\");\n        assert!(raw != null_val, \"{field} uses nullValue but field is required\");\n",
                                host = host_ty,
                                nullv = nullv,
                                field = field.name
                            ));
                    }
                    code.push_str(&format!(
                        "        let encoded = {expr};\n        write_bytes_into(self.buf, self.start + {offset}usize, &encoded).expect(\"group fixed field write in bounds\");\n        self\n    }}\n",
                        expr = encoded_expr,
                        offset = offset
                    ));
                } else {
                    code.push_str(&format!(
                        "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_into(self.buf, self.start + {offset}usize, &encoded).expect(\"group fixed field write in bounds\");\n        self\n    }}\n",
                        name = field.name.to_snake_case(),
                        param = param_ty,
                        expr = encoded_expr,
                        offset = offset
                    ));
                }
            } else {
                code.push_str(&format!(
                    "    pub fn {name}(&mut self, value: {param}) -> &mut Self {{\n        let encoded = {expr};\n        write_bytes_into(self.buf, self.start + {offset}usize, &encoded).expect(\"group fixed field write in bounds\");\n        self\n    }}\n",
                    name = field.name.to_snake_case(),
                    param = param_ty,
                    expr = encoded_expr,
                    offset = offset
                ));
            }
        }
    }
    for nested in &g_nested {
        let nested_encoder = format!("{}GroupEncoder", nested.name);
        let name = nested.name.to_snake_case();
        code.push_str(&format!(
            "    pub fn {name}<F>(&mut self, f: F) -> Result<&mut Self, EncodeIntoError>\n    where\n        F: FnOnce(&mut {encoder}<'_>) -> Result<(), EncodeIntoError>,\n    {{\n        let mut encoder = {encoder}::new(self.buf, self.cursor)?;\n        f(&mut encoder)?;\n        self.cursor = encoder.finish()?;\n        Ok(self)\n    }}\n",
            name = name,
            encoder = nested_encoder
        ));
    }
    for d in &g_data {
        let name = d.name.to_snake_case();
        let kind = length_kind_token(length_kind_for_data(&d.ty, schema, opts));
        code.push_str(&format!(
            "    pub fn {name}(&mut self, bytes: &[u8]) -> Result<&mut Self, EncodeIntoError> {{\n        let written = write_var_data_into(self.buf, self.cursor, bytes, {kind}, ENDIAN)?;\n        self.cursor = self.cursor.checked_add(written).ok_or(EncodeIntoError::InvalidState(\"group var data length overflow\"))?;\n        Ok(self)\n    }}\n",
            name = name,
            kind = kind
        ));
    }
    code.push_str("    pub fn finish(self) -> usize {\n        self.cursor\n    }\n");
    code.push_str("}\n\n");

    if has_any_var {
        code.push_str(&format!(
            "impl<'a> Iterator for {}<'a> {{\n    type Item = {}<'a>;\n    fn next(&mut self) -> Option<Self::Item> {{\n        if self.entries_left == 0 {{\n            return None;\n        }}\n        let blen = self.block_length;\n        if blen > self.remaining.len() {{\n            return None;\n        }}\n        let (entry, mut tail) = self.remaining.split_at(blen);\n        let (body, _) = {}::parse_prefix(entry)?;\n",
            iter_name, entry_view, entry_struct
        ));
        let mut data_idx = 0;
        for member in &g.members {
            match member {
                GroupMember::Group(nested) => {
                    code.push_str(&format!(
                        "        let nested_{name} = parse_{snake}(tail)?;\n",
                        name = nested.name.to_snake_case(),
                        snake = nested.name.to_snake_case()
                    ));
                    code.push_str(&format!(
                        "        let after_{name} = skip_{snake}(nested_{name}.payload, nested_{name}.count(), nested_{name}.block_length)?;\n        tail = after_{name};\n",
                        name = nested.name.to_snake_case(),
                        snake = nested.name.to_snake_case()
                    ));
                }
                GroupMember::Data(_) => {
                    code.push_str(&format!(
                        "        let (data_{idx}, after_{idx}) = parse_var_data(tail, *self.data_kinds.get({idx})?)?;\n        tail = after_{idx};\n",
                        idx = data_idx
                    ));
                    data_idx += 1;
                }
                GroupMember::Field(_) => {}
            }
        }
        code.push_str(
            "        self.remaining = tail;\n        self.entries_left -= 1;\n        Some(",
        );
        code.push_str(&format!("{} {{ body", entry_view));
        data_idx = 0;
        for member in &g.members {
            match member {
                GroupMember::Group(nested) => {
                    code.push_str(&format!(
                        ", {}: nested_{}",
                        nested.name.to_snake_case(),
                        nested.name.to_snake_case()
                    ));
                }
                GroupMember::Data(d) => {
                    code.push_str(&format!(", {}: data_{}", d.name.to_snake_case(), data_idx));
                    data_idx += 1;
                }
                GroupMember::Field(_) => {}
            }
        }
        code.push_str(" })\n    }\n}\n\n");
    } else {
        code.push_str(&format!("impl<'a> Iterator for {}<'a> {{\n", iter_name));
        code.push_str(&format!("    type Item = {}<'a>;\n", entry_view));
        code.push_str(
            "    fn next(&mut self) -> Option<Self::Item> {\n        if self.entries_left == 0 {\n            return None;\n        }\n        let blen = self.block_length;\n        if blen > self.remaining.len() {\n            return None;\n        }\n",
        );
        code.push_str(&format!(
            "        let (entry, tail) = self.remaining.split_at(blen);\n        let (body, _) = {}::parse_prefix(entry)?;\n",
            entry_struct
        ));
        code.push_str(
            "        self.remaining = tail;\n        self.entries_left -= 1;\n        Some(",
        );
        code.push_str(&format!("{} {{ body }}", entry_view));
        code.push_str(")\n    }\n}\n\n");
    }

    let has_var_members = g
        .members
        .iter()
        .any(|m| !matches!(m, GroupMember::Field(_)));
    code.push_str(&format!(
        "fn skip_{}<'a>(mut cursor: &'a [u8], count: usize, block_length: usize) -> Option<&'a [u8]> {{\n    for _ in 0..count {{\n",
        group_snake
    ));
    if !has_var_members {
        code.push_str("        if block_length == 0 { return None; }\n");
    }
    code.push_str(
        "        let mut tail = if block_length <= cursor.len() { &cursor[block_length..] } else { return None; };\n",
    );
    for member in g
        .members
        .iter()
        .filter(|m| !matches!(m, GroupMember::Field(_)))
    {
        match member {
            GroupMember::Group(nested) => {
                code.push_str(&format!(
                    "        let nested = parse_{}(tail)?;\n        tail = skip_{}(nested.payload, nested.count(), nested.block_length)?;\n",
                    nested.name.to_snake_case(),
                    nested.name.to_snake_case()
                ));
            }
            GroupMember::Data(d) => {
                let kind = length_kind_token(length_kind_for_data(&d.ty, schema, opts));
                code.push_str(&format!(
                    "        let (_, after_data) = parse_var_data(tail, {})?;\n        tail = after_data;\n",
                    kind
                ));
            }
            GroupMember::Field(_) => {}
        }
    }
    code.push_str("        cursor = tail;\n    }\n    Some(cursor)\n}\n\n");

    for nested in &g_nested {
        emit_group(nested, schema, opts, code);
    }
}

struct DimensionFields {
    block_field: String,
    block_field_ty: String,
    count_field: String,
    count_field_ty: String,
}

fn dimension_fields(schema: &Schema, dim_type: &str, opts: &GeneratorOptions) -> DimensionFields {
    if let Some(TypeDef::Composite { fields, .. }) = schema.types.get(dim_type) {
        let mut prim_fields = Vec::new();
        for f in fields {
            if let CompositeField::Type {
                name, primitive, ..
            } = f
                && let Some(rust) = primitive_to_rust(primitive, opts)
            {
                prim_fields.push((name.to_snake_case(), rust));
            }
        }
        let block = prim_fields
            .iter()
            .find(|(n, _)| n.contains("block_length") || n.contains("blocklength"))
            .cloned()
            .or_else(|| prim_fields.first().cloned())
            .unwrap_or_else(|| ("block_length".into(), "U16".into()));
        let mut count = prim_fields
            .iter()
            .find(|(n, _)| n.contains("num_in_group") || n.contains("numingroup"))
            .cloned()
            .or_else(|| prim_fields.get(1).cloned())
            .unwrap_or_else(|| ("num_in_group".into(), "U16".into()));
        // allow block and count to be the same when only one field exists: duplicate
        if block.0 == count.0 && prim_fields.len() == 1 {
            count = ("num_in_group".into(), block.1.clone());
        }
        return DimensionFields {
            block_field: block.0,
            block_field_ty: block.1,
            count_field: count.0,
            count_field_ty: count.1,
        };
    }
    DimensionFields {
        block_field: "block_length".into(),
        block_field_ty: "U16".into(),
        count_field: "num_in_group".into(),
        count_field_ty: "U16".into(),
    }
}

fn to_usize_expr(field_name: &str, ty: &str) -> String {
    match ty {
        "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "u64" | "i64" => {
            format!("{} as usize", field_name)
        }
        _ => format!("{}.get() as usize", field_name),
    }
}

fn field_primitive(field: &Field, schema: &Schema) -> Option<String> {
    if let Some(td) = schema.types.get(&field.ty) {
        match td {
            TypeDef::Primitive { primitive, .. } => Some(primitive.clone()),
            TypeDef::Enum { encoding, .. } | TypeDef::Set { encoding, .. } => {
                Some(encoding.clone())
            }
            TypeDef::Composite { .. } => None,
        }
    } else {
        Some(field.ty.clone())
    }
}

fn encoding_primitive(
    encoding: &str,
    types: &std::collections::HashMap<String, TypeDef>,
) -> Option<String> {
    if let Some(td) = types.get(encoding) {
        match td {
            TypeDef::Primitive { primitive, .. } => Some(primitive.clone()),
            TypeDef::Enum { encoding, .. } | TypeDef::Set { encoding, .. } => {
                encoding_primitive(encoding, types)
            }
            TypeDef::Composite { .. } => None,
        }
    } else {
        Some(encoding.to_string())
    }
}

fn optional_null_literal(prim: &str) -> Option<&'static str> {
    match prim {
        "boolean" | "uint8" | "char" => Some("u8::MAX"),
        "int8" => Some("i8::MIN"),
        "uint16" => Some("u16::MAX"),
        "int16" => Some("i16::MIN"),
        "uint32" => Some("u32::MAX"),
        "int32" => Some("i32::MIN"),
        "uint64" => Some("u64::MAX"),
        "int64" => Some("i64::MIN"),
        "float" => Some("f32::NAN"),
        "double" => Some("f64::NAN"),
        _ => None,
    }
}

fn optional_host_type(prim: &str) -> Option<&'static str> {
    match prim {
        "boolean" | "uint8" | "char" => Some("u8"),
        "int8" => Some("i8"),
        "uint16" => Some("u16"),
        "int16" => Some("i16"),
        "uint32" => Some("u32"),
        "int32" => Some("i32"),
        "uint64" => Some("u64"),
        "int64" => Some("i64"),
        "float" => Some("f32"),
        "double" => Some("f64"),
        _ => None,
    }
}

fn scalar_expr(expr: &str, rust_type: &str) -> Option<String> {
    let base = rust_type.rsplit("::").next().unwrap_or(rust_type);
    match base {
        "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "u64" | "i64" | "f32" | "f64" => {
            Some(expr.to_string())
        }
        "U16" | "I16" | "U32" | "I32" | "U64" | "I64" | "F32" | "F64" => {
            Some(format!("{}.get()", expr))
        }
        _ => None,
    }
}

fn type_is_optional(ty: &str, schema: &Schema) -> bool {
    matches!(
        schema.types.get(ty),
        Some(TypeDef::Primitive {
            presence: Some(p),
            ..
        }) if p == "optional"
    ) || matches!(
        schema.types.get(ty),
        Some(TypeDef::Primitive {
            null_value: Some(_),
            ..
        })
    )
}

fn type_null_value<'a>(ty: &str, schema: &'a Schema) -> Option<&'a str> {
    match schema.types.get(ty) {
        Some(TypeDef::Primitive { null_value, .. }) => null_value.as_deref(),
        _ => None,
    }
}

fn type_length(ty: &str, schema: &Schema) -> Option<usize> {
    match schema.types.get(ty) {
        Some(TypeDef::Primitive { length, .. }) => *length,
        _ => None,
    }
}

fn type_primitive<'a>(ty: &str, schema: &'a Schema) -> Option<&'a str> {
    match schema.types.get(ty) {
        Some(TypeDef::Primitive { primitive, .. }) => Some(primitive.as_str()),
        _ => None,
    }
}

fn field_is_constant(field: &Field, schema: &Schema) -> bool {
    if field.presence.as_deref() == Some("constant") {
        return true;
    }
    if field.presence.is_some() {
        return false;
    }
    matches!(
        schema.types.get(&field.ty),
        Some(TypeDef::Primitive {
            presence: Some(p),
            ..
        }) if p == "constant"
    )
}

fn push_constant_field_impl(
    code: &mut String,
    struct_name: &str,
    fields: &[&Field],
    schema: &Schema,
    opts: &GeneratorOptions,
) {
    let const_fields: Vec<_> = fields
        .iter()
        .copied()
        .filter(|f| field_is_constant(f, schema))
        .collect();
    if const_fields.is_empty() {
        return;
    }
    code.push_str(&format!("impl {} {{\n", struct_name));
    for f in const_fields {
        let field_name = f.name.to_snake_case();
        let ty = &f.ty;
        if let Some(vr) = &f.value_ref {
            let parts: Vec<&str> = vr.split('.').collect();
            if parts.len() == 2 {
                let type_name = parts[0];
                let variant = parts[1];
                code.push_str(&format!(
                    "    pub const {}: {} = {}::{};\n",
                    field_name.to_uppercase(),
                    type_name,
                    type_name,
                    variant
                ));
                continue;
            }
        }
        if let Some(TypeDef::Primitive {
            constant: Some(val),
            primitive,
            length,
            ..
        }) = schema.types.get(ty)
        {
            let rust_type = match primitive_to_rust(primitive, opts) {
                Some(t) => t,
                None => continue,
            };
            if let Some(len) = length {
                if let Some((_, expr)) = const_array_expr(primitive, &rust_type, val, *len) {
                    code.push_str(&format!(
                        "    pub const {}: {} = {};\n",
                        field_name.to_uppercase(),
                        ty,
                        expr
                    ));
                }
            } else if let Some(raw_expr) = const_scalar_expr(primitive, &rust_type, val) {
                let alias_target = opts
                    .constant_type_aliases
                    .get(ty)
                    .cloned()
                    .or_else(|| constant_type_alias_from_schema(ty, schema))
                    .unwrap_or_else(|| rust_type.clone());
                let expr = if matches!(
                    schema.types.get(&alias_target),
                    Some(TypeDef::Enum { .. } | TypeDef::Set { .. })
                ) {
                    format!("{}({})", alias_target, raw_expr)
                } else {
                    raw_expr
                };
                code.push_str(&format!(
                    "    pub const {}: {} = {};\n",
                    field_name.to_uppercase(),
                    ty,
                    expr
                ));
            }
        }
    }
    code.push_str("}\n\n");
}

fn field_is_optional(field: &Field, schema: &Schema) -> bool {
    match field.presence.as_deref() {
        Some("optional") => true,
        Some("constant") | Some("required") => false,
        _ => field.null_value.is_some() || type_is_optional(&field.ty, schema),
    }
}

fn field_null_value<'a>(field: &'a Field, schema: &'a Schema) -> Option<&'a str> {
    field
        .null_value
        .as_deref()
        .or_else(|| type_null_value(&field.ty, schema))
}

fn const_host_expr(prim: &str, raw: &str) -> Option<String> {
    let trimmed = raw.trim();
    let expr = match prim {
        "char" => {
            let byte = trimmed.as_bytes().first().copied().unwrap_or(0);
            format!("{}u8", byte)
        }
        "boolean" => match trimmed {
            "true" => "1u8".to_string(),
            "false" => "0u8".to_string(),
            _ => trimmed.to_string(),
        },
        "float" => {
            if trimmed.eq_ignore_ascii_case("nan") {
                "f32::NAN".to_string()
            } else {
                trimmed.to_string()
            }
        }
        "double" => {
            if trimmed.eq_ignore_ascii_case("nan") {
                "f64::NAN".to_string()
            } else {
                trimmed.to_string()
            }
        }
        _ => trimmed.to_string(),
    };
    Some(expr)
}

fn null_cond_for_primitive(
    prim: &str,
    host_type: &str,
    null_value: Option<&str>,
    use_default: bool,
) -> Option<String> {
    let is_float = host_type == "f32" || host_type == "f64";
    if is_float {
        if null_value.is_none() && use_default {
            return Some("raw.is_nan()".to_string());
        }
        if let Some(val) = null_value
            && val.trim().eq_ignore_ascii_case("nan")
        {
            return Some("raw.is_nan()".to_string());
        }
    }

    let raw = if let Some(val) = null_value {
        Some(val)
    } else if use_default {
        optional_null_literal(prim)
    } else {
        None
    }?;

    let expr = const_host_expr(prim, raw)?;
    Some(format!("raw == {}", expr))
}

fn optional_methods_for_fields(
    code: &mut String,
    fields: &[&Field],
    schema: &Schema,
    opts: &GeneratorOptions,
    self_expr: &str,
) {
    for field in fields {
        if !field_is_optional(field, schema) {
            continue;
        }
        let field_name = field.name.to_snake_case();
        let method_name = format!("{}_opt", field_name);

        if let Some(len) = type_length(&field.ty, schema)
            && len > 1
        {
            continue;
        }

        // Enum/set optional: return Option<EnumName> by inspecting the inner primitive
        if let Some(TypeDef::Enum { encoding, .. } | TypeDef::Set { encoding, .. }) =
            schema.types.get(&field.ty)
        {
            let inner_rust =
                match primitive_to_rust_endian(encoding, &opts.endian, field.byte_order.as_deref())
                {
                    Some(t) => t,
                    None => continue,
                };
            if let Some(val_expr) =
                scalar_expr(&format!("{}.{}.0", self_expr, field_name), &inner_rust)
            {
                let host_type = match optional_host_type(encoding) {
                    Some(t) => t,
                    None => continue,
                };
                let cond = match null_cond_for_primitive(
                    encoding,
                    host_type,
                    field_null_value(field, schema),
                    true,
                ) {
                    Some(c) => c,
                    None => continue,
                };
                code.push_str(&format!(
                    "    pub fn {method}(&self) -> Option<{ty}> {{\n        let v = {self_expr}.{field};\n        let raw = {val};\n        if {cond} {{ None }} else {{ Some(v) }}\n    }}\n",
                    method = method_name,
                    ty = field.ty,
                    self_expr = self_expr,
                    field = field_name,
                    val = val_expr,
                    cond = cond,
                ));
            }
            continue;
        }

        let prim = match field_primitive(field, schema) {
            Some(p) => p,
            None => continue,
        };
        let host_type = match optional_host_type(&prim) {
            Some(t) => t,
            None => continue,
        };

        let inner_rust =
            match primitive_to_rust_endian(&prim, &opts.endian, field.byte_order.as_deref()) {
                Some(t) => t,
                None => continue,
            };
        let scalar = scalar_expr(&format!("{}.{}", self_expr, field_name), &inner_rust);
        if let Some(val_expr) = scalar {
            let cond = match null_cond_for_primitive(
                &prim,
                host_type,
                field_null_value(field, schema),
                true,
            ) {
                Some(c) => c,
                None => continue,
            };
            code.push_str(&format!(
                "    pub fn {method}(&self) -> Option<{ty}> {{\n        let raw = {val};\n        if {cond} {{ None }} else {{ Some(raw) }}\n    }}\n",
                method = method_name,
                ty = host_type,
                val = val_expr,
                cond = cond,
            ));
        }
    }
}

fn optional_methods_for_composite_fields(
    code: &mut String,
    fields: &[CompositeField],
    schema: &Schema,
    opts: &GeneratorOptions,
    self_expr: &str,
) {
    for field in fields {
        match field {
            CompositeField::Type {
                name,
                primitive,
                length,
                presence,
                null_value,
                ..
            } => {
                if presence.as_deref() == Some("constant") {
                    continue;
                }
                let is_optional = match presence.as_deref() {
                    Some("optional") => true,
                    Some("required") => false,
                    _ => null_value.is_some(),
                };
                if !is_optional {
                    continue;
                }
                if let Some(len) = length
                    && *len > 1
                {
                    continue;
                }
                let rust_type = match primitive_to_rust(primitive, opts) {
                    Some(t) => t,
                    None => continue,
                };
                let val_expr = match scalar_expr(
                    &format!("{}.{}", self_expr, name.to_snake_case()),
                    &rust_type,
                ) {
                    Some(v) => v,
                    None => continue,
                };
                let host_type = match optional_host_type(primitive) {
                    Some(t) => t,
                    None => continue,
                };
                let cond = match null_cond_for_primitive(
                    primitive,
                    host_type,
                    null_value.as_deref(),
                    true,
                ) {
                    Some(c) => c,
                    None => continue,
                };
                let method_name = format!("{}_opt", name.to_snake_case());
                code.push_str(&format!(
                    "    pub fn {method}(&self) -> Option<{ty}> {{\n        let raw = {val};\n        if {cond} {{ None }} else {{ Some(raw) }}\n    }}\n",
                    method = method_name,
                    ty = host_type,
                    val = val_expr,
                    cond = cond,
                ));
            }
            CompositeField::Ref { name, ty } => {
                if !type_is_optional(ty, schema) {
                    continue;
                }
                if let Some(len) = type_length(ty, schema)
                    && len > 1
                {
                    continue;
                }
                let prim = match type_primitive(ty, schema) {
                    Some(p) => p,
                    None => continue,
                };
                let rust_type = match primitive_to_rust(prim, opts) {
                    Some(t) => t,
                    None => continue,
                };
                let val_expr = match scalar_expr(
                    &format!("{}.{}", self_expr, name.to_snake_case()),
                    &rust_type,
                ) {
                    Some(v) => v,
                    None => continue,
                };
                let host_type = match optional_host_type(prim) {
                    Some(t) => t,
                    None => continue,
                };
                let cond = match null_cond_for_primitive(
                    prim,
                    host_type,
                    type_null_value(ty, schema),
                    true,
                ) {
                    Some(c) => c,
                    None => continue,
                };
                let method_name = format!("{}_opt", name.to_snake_case());
                code.push_str(&format!(
                    "    pub fn {method}(&self) -> Option<{ty}> {{\n        let raw = {val};\n        if {cond} {{ None }} else {{ Some(raw) }}\n    }}\n",
                    method = method_name,
                    ty = host_type,
                    val = val_expr,
                    cond = cond,
                ));
            }
        }
    }
}

fn composite_null_constants(
    code: &mut String,
    fields: &[CompositeField],
    schema: &Schema,
    opts: &GeneratorOptions,
) {
    for field in fields {
        match field {
            CompositeField::Type {
                name,
                primitive,
                length,
                presence,
                null_value,
                ..
            } => {
                if presence.as_deref() == Some("constant") {
                    continue;
                }
                let null_raw = null_value.as_deref().or_else(|| {
                    if presence.as_deref() == Some("optional") && length.is_none() {
                        optional_null_literal(primitive)
                    } else {
                        None
                    }
                });
                let null_raw = match null_raw {
                    Some(v) => v,
                    None => continue,
                };
                let rust_type = match primitive_to_rust(primitive, opts) {
                    Some(t) => t,
                    None => continue,
                };
                let cname = format!("{}_NULL", name.to_uppercase());
                if let Some(len) = length {
                    if null_value.is_none() {
                        continue;
                    }
                    if let Some((ty, expr)) =
                        const_array_expr(primitive, &rust_type, null_raw, *len)
                    {
                        code.push_str(&format!("    pub const {}: {} = {};\n", cname, ty, expr));
                    }
                } else if let Some(expr) = const_scalar_expr(primitive, &rust_type, null_raw) {
                    code.push_str(&format!(
                        "    pub const {}: {} = {};\n",
                        cname, rust_type, expr
                    ));
                }
            }
            CompositeField::Ref { name, ty } => {
                let (prim, length, presence, null_value) = if let Some(TypeDef::Primitive {
                    primitive,
                    length,
                    presence,
                    null_value,
                    ..
                }) = schema.types.get(ty)
                {
                    (
                        primitive.as_str(),
                        *length,
                        presence.as_deref(),
                        null_value.as_deref(),
                    )
                } else {
                    continue;
                };
                let is_optional = presence == Some("optional") || null_value.is_some();
                if !is_optional {
                    continue;
                }
                let null_raw = null_value.or_else(|| {
                    if presence == Some("optional") && length.is_none() {
                        optional_null_literal(prim)
                    } else {
                        None
                    }
                });
                let null_raw = match null_raw {
                    Some(v) => v,
                    None => continue,
                };
                let rust_type = match primitive_to_rust(prim, opts) {
                    Some(t) => t,
                    None => continue,
                };
                let cname = format!("{}_NULL", name.to_uppercase());
                if let Some(len) = length {
                    if null_value.is_none() {
                        continue;
                    }
                    if let Some((_, expr)) = const_array_expr(prim, &rust_type, null_raw, len) {
                        code.push_str(&format!("    pub const {}: {} = {};\n", cname, ty, expr));
                    }
                } else if let Some(expr) = const_scalar_expr(prim, &rust_type, null_raw) {
                    code.push_str(&format!("    pub const {}: {} = {};\n", cname, ty, expr));
                }
            }
        }
    }
}

fn block_length_from_fields(
    fields: &[&Field],
    schema: &Schema,
    opts: &GeneratorOptions,
) -> Option<usize> {
    let mut cur = 0usize;
    let mut max_end = 0usize;
    for field in fields {
        if field_is_constant(field, schema) {
            continue;
        }
        let sz = field_size_bytes(field, schema, opts)?;
        let start = field.offset.map(|o| o as usize).unwrap_or(cur);
        let end = start + sz;
        max_end = max_end.max(end);
        cur = end;
    }
    Some(max_end)
}

fn message_block_length(msg: &Message, schema: &Schema, opts: &GeneratorOptions) -> usize {
    msg.block_length
        .map(|b| b as usize)
        .or_else(|| block_length_from_fields(&message_fields(msg), schema, opts))
        .unwrap_or(0)
}

fn group_block_length(g: &Group, schema: &Schema, opts: &GeneratorOptions) -> usize {
    g.block_length
        .map(|b| b as usize)
        .or_else(|| block_length_from_fields(&group_fields(g), schema, opts))
        .unwrap_or(0)
}

struct FieldLayout<'a> {
    field: &'a Field,
    offset: usize,
    size: usize,
}

fn layout_fields<'a>(
    fields: &[&'a Field],
    schema: &Schema,
    opts: &GeneratorOptions,
) -> Vec<FieldLayout<'a>> {
    let mut cur = 0usize;
    let mut layout = Vec::new();
    for f in fields {
        if field_is_constant(f, schema) {
            continue;
        }
        if resolve_type(&f.ty, schema, opts, f.byte_order.as_deref()).is_none() {
            continue;
        }
        if let Some(sz) = field_size_bytes(f, schema, opts) {
            let start = f.offset.map(|o| o as usize).unwrap_or(cur);
            layout.push(FieldLayout {
                field: f,
                offset: start,
                size: sz,
            });
            cur = start + sz;
        }
    }
    layout
}

fn composite_field_offset(
    ty: &str,
    field_name: &str,
    schema: &Schema,
    opts: &GeneratorOptions,
) -> Option<usize> {
    let target = field_name.to_snake_case();
    if let Some(TypeDef::Composite { fields, .. }) = schema.types.get(ty) {
        let mut cur = 0usize;
        for f in fields {
            match f {
                CompositeField::Type {
                    name,
                    primitive,
                    length,
                    presence,
                    ..
                } => {
                    if name.to_snake_case() == target {
                        return Some(cur);
                    }
                    if presence.as_deref() == Some("constant") {
                        continue;
                    }
                    if let Some(sz) = primitive_size_bytes(primitive) {
                        cur += sz * length.unwrap_or(1);
                    } else {
                        return None;
                    }
                }
                CompositeField::Ref { name, ty } => {
                    if name.to_snake_case() == target {
                        return Some(cur);
                    }
                    cur += type_size_bytes(ty, schema, opts, 0, true)?;
                }
            }
        }
    }
    None
}

fn builder_param_type(resolved_type: &str) -> String {
    let trimmed = resolved_type.trim();
    if trimmed.starts_with('[') {
        return trimmed.to_string();
    }
    let base = trimmed.rsplit("::").next().unwrap_or(trimmed);
    match base {
        "U16" => "u16".into(),
        "I16" => "i16".into(),
        "U32" => "u32".into(),
        "I32" => "i32".into(),
        "U64" => "u64".into(),
        "I64" => "i64".into(),
        "F32" => "f32".into(),
        "F64" => "f64".into(),
        "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "u64" | "i64" | "f32" | "f64" => {
            trimmed.to_string()
        }
        _ => trimmed.to_string(),
    }
}

fn builder_value_expr(param_name: &str, resolved_type: &str) -> String {
    let trimmed = resolved_type.trim();
    if trimmed.starts_with('[') {
        return param_name.to_string();
    }
    let base = trimmed.rsplit("::").next().unwrap_or(trimmed);
    match base {
        "U16" | "I16" | "U32" | "I32" | "U64" | "I64" | "F32" | "F64" => {
            format!("{}::new({})", trimmed, param_name)
        }
        "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "u64" | "i64" | "f32" | "f64" => {
            format!("({}) as {}", param_name, base)
        }
        _ => param_name.to_string(),
    }
}

fn set_bit_value_expr(bit_idx: u32, rust_type: &str) -> String {
    let base = rust_type.rsplit("::").next().unwrap_or(rust_type);
    match base {
        "U16" => format!("U16::new({})", bit_cast_expr(bit_idx, "u16")),
        "I16" => format!("I16::new({})", bit_cast_expr(bit_idx, "i16")),
        "U32" => format!("U32::new({})", bit_cast_expr(bit_idx, "u32")),
        "I32" => format!("I32::new({})", bit_cast_expr(bit_idx, "i32")),
        "U64" => format!("U64::new({})", bit_cast_expr(bit_idx, "u64")),
        "I64" => format!("I64::new({})", bit_cast_expr(bit_idx, "i64")),
        "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "u64" | "i64" => bit_cast_expr(bit_idx, base),
        _ => format!("(1u64 << {})", bit_idx),
    }
}

fn bit_cast_expr(bit_idx: u32, base: &str) -> String {
    let shift = format!("(1u64 << {})", bit_idx);
    match base {
        "u8" => format!("{} as u8", shift),
        "i8" => format!("{} as i8", shift),
        "u16" => format!("{} as u16", shift),
        "i16" => format!("{} as i16", shift),
        "u32" => format!("{} as u32", shift),
        "i32" => format!("{} as i32", shift),
        "u64" => shift,
        "i64" => format!("{} as i64", shift),
        _ => shift,
    }
}

fn count_value_expr(count_name: &str, ty: &str) -> String {
    let trimmed = ty.trim();
    let base = trimmed.rsplit("::").next().unwrap_or(trimmed);
    match base {
        "U16" => format!(
            "{}::new(u16::try_from({}).expect(\"count fits in u16\"))",
            trimmed, count_name
        ),
        "I16" => format!(
            "{}::new(i16::try_from({}).expect(\"count fits in i16\"))",
            trimmed, count_name
        ),
        "U32" => format!(
            "{}::new(u32::try_from({}).expect(\"count fits in u32\"))",
            trimmed, count_name
        ),
        "I32" => format!(
            "{}::new(i32::try_from({}).expect(\"count fits in i32\"))",
            trimmed, count_name
        ),
        "U64" => format!(
            "{}::new(u64::try_from({}).expect(\"count fits in u64\"))",
            trimmed, count_name
        ),
        "I64" => format!(
            "{}::new(i64::try_from({}).expect(\"count fits in i64\"))",
            trimmed, count_name
        ),
        "u8" => format!("u8::try_from({}).expect(\"count fits in u8\")", count_name),
        "i8" => format!("i8::try_from({}).expect(\"count fits in i8\")", count_name),
        "u16" => format!(
            "u16::try_from({}).expect(\"count fits in u16\")",
            count_name
        ),
        "i16" => format!(
            "i16::try_from({}).expect(\"count fits in i16\")",
            count_name
        ),
        "u32" => format!(
            "u32::try_from({}).expect(\"count fits in u32\")",
            count_name
        ),
        "i32" => format!(
            "i32::try_from({}).expect(\"count fits in i32\")",
            count_name
        ),
        "u64" => format!(
            "u64::try_from({}).expect(\"count fits in u64\")",
            count_name
        ),
        "i64" => format!(
            "i64::try_from({}).expect(\"count fits in i64\")",
            count_name
        ),
        _ => format!("{}.try_into().expect(\"count fits\")", count_name),
    }
}

fn host_type_for_field(field: &Field, schema: &Schema) -> Option<String> {
    let prim = field_primitive(field, schema)?;
    optional_host_type(&prim).map(|s| s.to_string())
}

fn raw_expr_for_value(
    field: &Field,
    val_ident: &str,
    schema: &Schema,
    opts: &GeneratorOptions,
) -> Option<String> {
    if let Some(TypeDef::Enum { encoding, .. } | TypeDef::Set { encoding, .. }) =
        schema.types.get(&field.ty)
    {
        let inner_rust =
            primitive_to_rust_endian(encoding, &opts.endian, field.byte_order.as_deref())?;
        scalar_expr(&format!("{}.0", val_ident), &inner_rust)
    } else {
        Some(val_ident.to_string())
    }
}
