Skip to main content

synta_codegen/
codegen.rs

1//! Rust code generator from ASN.1 AST
2
3use crate::ast::*;
4use std::collections::{HashMap, HashSet};
5use std::fmt::Write;
6
7/// Controls whether string and binary ASN.1 types are generated as owned
8/// heap-allocating types or as zero-copy borrowed references.
9///
10/// The following ASN.1 types have both an owned form and a zero-copy `Ref`
11/// variant and are therefore affected by this setting:
12///
13/// | ASN.1 type        | Owned (`Owned` mode)  | Borrowed (`Borrowed` mode) |
14/// |-------------------|-----------------------|----------------------------|
15/// | `OCTET STRING`    | `OctetString`         | `OctetStringRef<'a>`       |
16/// | `BIT STRING`      | `BitString`           | `BitStringRef<'a>`         |
17/// | `UTF8String`      | `Utf8String`          | `Utf8StringRef<'a>`        |
18/// | `PrintableString` | `PrintableString`     | `PrintableStringRef<'a>`   |
19/// | `IA5String`       | `IA5String`           | `IA5StringRef<'a>`         |
20///
21/// Types that have **no** zero-copy variant (`TeletexString`, `BmpString`,
22/// `UniversalString`, `GeneralString`, `NumericString`, `VisibleString`) are
23/// always emitted as owned types regardless of this setting.
24///
25/// Named bit strings (`BIT STRING { flag(0), … }`) are always emitted as
26/// owned `BitString` regardless of this setting because they are decoded into
27/// a concrete bit-field type.
28///
29/// - [`StringTypeMode::Owned`] (default): heap-allocates on decode; convenient
30///   for constructing structs from scratch (e.g. in tests or protocol message
31///   builders).
32/// - [`StringTypeMode::Borrowed`]: borrows directly from the input buffer;
33///   optimal for parse-only workloads such as X.509 certificate inspection.
34///   Every struct that contains these fields (directly or transitively) gains a
35///   `'a` lifetime parameter.
36#[derive(Debug, Clone, Default, PartialEq, Eq)]
37pub enum StringTypeMode {
38    /// Generate owned, heap-allocating types (`OctetString`, `BitString`, …).
39    /// No lifetime parameter is added to generated structs.
40    #[default]
41    Owned,
42    /// Generate zero-copy borrowed types (`OctetStringRef<'a>`, `BitStringRef<'a>`, …)
43    /// that borrow from the decoder's input buffer.
44    /// Structs containing these fields acquire a `'a` lifetime parameter.
45    Borrowed,
46}
47
48/// Controls how derive macros for ASN.1 types are emitted in generated code.
49///
50/// The generated code uses `synta_derive` proc-macros (`Asn1Sequence`,
51/// `Asn1Choice`, `Asn1Set`) as well as helper attributes (`asn1(tag(…))`,
52/// `asn1(optional)`, `asn1(rawder)`).  By default these are wrapped in
53/// `#[cfg_attr(feature = "derive", …)]` so that the consuming crate can make
54/// `synta-derive` an optional dependency.  Third-party crates that always
55/// depend on `synta-derive` can use [`DeriveMode::Always`] to emit the
56/// attributes unconditionally, removing the need to declare a `derive`
57/// Cargo feature.
58#[derive(Debug, Clone, Default, PartialEq, Eq)]
59pub enum DeriveMode {
60    /// Emit `#[cfg_attr(feature = "derive", derive(Asn1Sequence))]` (default).
61    ///
62    /// Derive macros and their helper attributes are gated behind a `derive`
63    /// Cargo feature.  The consuming crate must declare
64    /// `[features] derive = ["dep:synta-derive"]` (or similar) in its
65    /// `Cargo.toml`.
66    #[default]
67    FeatureGated,
68    /// Emit `#[derive(Asn1Sequence)]` unconditionally — no feature gate.
69    ///
70    /// Use this when the consuming crate always depends on `synta-derive`
71    /// and does not want to expose a `derive` Cargo feature.
72    Always,
73    /// Emit `#[cfg_attr(feature = "<name>", derive(Asn1Sequence))]` with a
74    /// custom feature name instead of `"derive"`.
75    ///
76    /// Useful when the consuming crate exposes its own feature name for
77    /// derive support (e.g. `"asn1-derive"` or `"full"`).
78    Custom(String),
79}
80
81/// Configuration options for code generation
82#[derive(Debug, Clone, Default)]
83pub struct CodeGenConfig {
84    /// Module path prefix for imports (e.g., "crate", "super", or custom path)
85    /// If None, imports are only documented in comments
86    pub module_path_prefix: Option<String>,
87    /// Emit `core::convert::TryFrom` instead of `std::convert::TryFrom`.
88    /// Use this when generating code for `#![no_std]` environments.
89    pub use_core: bool,
90    /// Set of imported type names that should be skipped during code generation
91    /// Types in this set are assumed to come from external modules
92    pub skip_imported_types: std::collections::HashSet<String>,
93    /// Map of imported type names to their lifetime requirements
94    /// Key: type name, Value: lifetime parameter (e.g., "'a")
95    /// If a type is not in this map, it's assumed to not require a lifetime
96    pub imported_type_lifetimes: std::collections::HashMap<String, String>,
97    /// Controls whether string/binary ASN.1 types (`OCTET STRING`, `BIT STRING`,
98    /// `UTF8String`, `PrintableString`, `IA5String`) are generated as owned
99    /// heap-allocating types or as zero-copy borrowed `Ref` variants.
100    /// Defaults to [`StringTypeMode::Owned`].
101    pub string_type_mode: StringTypeMode,
102    /// When `true`, `ANY` and `ANY DEFINED BY` fields are generated as
103    /// `RawDer<'a>` (zero-copy raw TLV capture) instead of `Element<'a>`.
104    ///
105    /// Use this for schemas where every open-typed field should be stored as raw
106    /// bytes for lazy decoding rather than eagerly parsed into an `Element` tree.
107    /// The generated type aliases and struct fields are then `RawDer<'a>`, which
108    /// implements both `Decode<'a>` (reads any TLV) and `DecodeImplicit<'a>`
109    /// (captures the value bytes of an IMPLICIT-tagged field).
110    ///
111    /// Defaults to `false`.
112    pub any_as_raw_der: bool,
113    /// Controls how derive macros (`Asn1Sequence`, `Asn1Choice`, `Asn1Set`)
114    /// and their helper attributes are emitted.
115    ///
116    /// Defaults to [`DeriveMode::FeatureGated`], which wraps every derive
117    /// annotation in `#[cfg_attr(feature = "derive", …)]`.  Set to
118    /// [`DeriveMode::Always`] to emit them unconditionally, which removes the
119    /// need for the consuming crate to declare a `derive` Cargo feature.
120    pub derive_mode: DeriveMode,
121    /// Set of field names (after `to_snake_case` conversion) that should be
122    /// emitted as `RawDer<'a>` regardless of their ASN.1 type.
123    ///
124    /// Use this to defer decoding of expensive fields — such as `issuer`,
125    /// `subject`, and `extensions` in an X.509 `TBSCertificate` — until the
126    /// caller explicitly requests them.  The field is stored as a zero-copy
127    /// TLV capture that can be decoded lazily.
128    ///
129    /// Fields listed here are always emitted with the `'a` lifetime, which
130    /// is propagated to the enclosing struct declaration automatically.
131    ///
132    /// Defaults to an empty set (no override).
133    pub raw_der_fields: std::collections::HashSet<String>,
134    /// Set of type names that are known `CHOICE` types, even when they are
135    /// defined in an imported (external) schema that is not available to this
136    /// codegen invocation.
137    ///
138    /// Per ASN.1 X.680 §31.2.7, `IMPLICIT` tagging cannot be applied to
139    /// `CHOICE` types; such tags must be treated as `EXPLICIT`.  When the
140    /// `CHOICE` definition lives in another schema file the codegen cannot
141    /// detect the tag-promotion requirement automatically.  Adding the type
142    /// name here extends `resolves_to_choice()` so that imported `CHOICE`
143    /// types are still promoted correctly.
144    ///
145    /// Example: `config.known_choice_types.insert("GeneralName".into());`
146    ///
147    /// Defaults to an empty set.
148    pub known_choice_types: std::collections::HashSet<String>,
149}
150
151impl CodeGenConfig {
152    /// Add a type that requires a lifetime parameter when imported
153    pub fn with_lifetime(
154        mut self,
155        type_name: impl Into<String>,
156        lifetime: impl Into<String>,
157    ) -> Self {
158        self.imported_type_lifetimes
159            .insert(type_name.into(), lifetime.into());
160        self
161    }
162
163    /// Configure common X.509 types that require lifetimes
164    /// This also marks which types should be skipped (imported from external crates)
165    /// vs which types should be generated locally
166    pub fn with_x509_lifetimes(mut self) -> Self {
167        // Types that are actually exported from synta-certificate and should be skipped
168        let skip_types = vec![
169            "AlgorithmIdentifier",
170            "SubjectPublicKeyInfo",
171            "TBSCertificate",
172            "Certificate",
173            "Validity", // Doesn't need lifetime but is imported
174        ];
175
176        // Imported types that need lifetime parameters (NOT locally-generated types)
177        let types_with_lifetimes = vec![
178            "AlgorithmIdentifier",
179            "SubjectPublicKeyInfo",
180            "TBSCertificate",
181            "Certificate",
182            // Note: Name and Extension are NOT in this list because they're defined
183            // locally without lifetimes to match the auto-generated X.509 code
184        ];
185
186        for type_name in skip_types {
187            self.skip_imported_types.insert(type_name.to_string());
188        }
189
190        for type_name in types_with_lifetimes {
191            self.imported_type_lifetimes
192                .insert(type_name.to_string(), "'a".to_string());
193        }
194
195        self
196    }
197}
198
199impl CodeGenConfig {
200    /// Create config with crate-relative imports
201    pub fn with_crate_imports() -> Self {
202        Self {
203            module_path_prefix: Some("crate".to_string()),
204            ..Default::default()
205        }
206    }
207
208    /// Create config with super-relative imports
209    pub fn with_super_imports() -> Self {
210        Self {
211            module_path_prefix: Some("super".to_string()),
212            ..Default::default()
213        }
214    }
215
216    /// Create config with custom module path prefix
217    pub fn with_custom_prefix(prefix: impl Into<String>) -> Self {
218        Self {
219            module_path_prefix: Some(prefix.into()),
220            ..Default::default()
221        }
222    }
223}
224
225/// Check if a name is a Rust keyword and needs escaping
226fn is_rust_keyword(s: &str) -> bool {
227    matches!(
228        s,
229        "as" | "break"
230            | "const"
231            | "continue"
232            | "crate"
233            | "else"
234            | "enum"
235            | "extern"
236            | "false"
237            | "fn"
238            | "for"
239            | "if"
240            | "impl"
241            | "in"
242            | "let"
243            | "loop"
244            | "match"
245            | "mod"
246            | "move"
247            | "mut"
248            | "pub"
249            | "ref"
250            | "return"
251            | "self"
252            | "Self"
253            | "static"
254            | "struct"
255            | "super"
256            | "trait"
257            | "true"
258            | "type"
259            | "unsafe"
260            | "use"
261            | "where"
262            | "while"
263            | "async"
264            | "await"
265            | "dyn"
266            | "abstract"
267            | "become"
268            | "box"
269            | "do"
270            | "final"
271            | "macro"
272            | "override"
273            | "priv"
274            | "typeof"
275            | "unsized"
276            | "virtual"
277            | "yield"
278            | "try"
279    )
280}
281
282/// Escape a Rust identifier if it's a keyword
283fn escape_rust_keyword(s: String) -> String {
284    if is_rust_keyword(&s) {
285        format!("r#{}", s)
286    } else {
287        s
288    }
289}
290
291/// Convert ASN.1 identifier to Rust snake_case
292fn to_snake_case(s: &str) -> String {
293    let mut result = String::new();
294    let mut prev_lower = false;
295
296    for (i, ch) in s.chars().enumerate() {
297        if ch == '-' {
298            result.push('_');
299            prev_lower = false;
300        } else if ch.is_uppercase() {
301            if i > 0 && prev_lower {
302                result.push('_');
303            }
304            result.push(ch.to_ascii_lowercase());
305            prev_lower = false;
306        } else {
307            result.push(ch);
308            prev_lower = ch.is_lowercase();
309        }
310    }
311
312    escape_rust_keyword(result)
313}
314
315/// Convert ASN.1 identifier to Rust PascalCase.
316///
317/// Each hyphen-separated segment is capitalised independently.  If a segment
318/// consists entirely of uppercase letters (and optional digits / other
319/// non-letter characters), the non-first letter characters are lowercased so
320/// that e.g. `KDC-REQ` becomes `KdcReq` rather than `KDCREQ`.  Mixed-case
321/// segments such as `Kerberos` or `SafeBody` are left unchanged (only the
322/// first character is forced to uppercase).
323fn to_pascal_case(s: &str) -> String {
324    s.split('-').map(pascal_segment).collect()
325}
326
327/// Capitalise one hyphen-separated segment for use in a PascalCase identifier.
328fn pascal_segment(seg: &str) -> String {
329    if seg.is_empty() {
330        return String::new();
331    }
332    // A segment is "all-caps" when every *letter* character is uppercase.
333    // Digits and other non-letter characters are neutral.
334    let all_caps = seg.chars().all(|c| !c.is_alphabetic() || c.is_uppercase());
335    let mut out = String::with_capacity(seg.len());
336    for (i, c) in seg.chars().enumerate() {
337        if i == 0 {
338            out.push(c.to_ascii_uppercase());
339        } else if all_caps && c.is_alphabetic() {
340            out.push(c.to_ascii_lowercase());
341        } else {
342            out.push(c);
343        }
344    }
345    out
346}
347
348/// Convert ASN.1 identifier to Rust SCREAMING_SNAKE_CASE for constants
349fn to_screaming_snake_case(s: &str) -> String {
350    let mut result = String::new();
351    let mut prev_lower = false;
352
353    for (i, ch) in s.chars().enumerate() {
354        if ch == '-' {
355            result.push('_');
356            prev_lower = false;
357        } else if ch.is_ascii_uppercase() {
358            if i > 0 && prev_lower {
359                result.push('_');
360            }
361            result.push(ch);
362            prev_lower = false;
363        } else {
364            result.push(ch.to_ascii_uppercase());
365            prev_lower = true;
366        }
367    }
368
369    result
370}
371
372/// Convert ASN.1 module name to Rust module name (snake_case)
373fn module_name_to_rust(s: &str) -> String {
374    // Delegate to to_snake_case so that Rust keywords are properly escaped
375    // and the conversion logic stays in one place.
376    to_snake_case(s)
377}
378
379/// Code generator
380pub struct CodeGenerator {
381    output: String,
382    config: CodeGenConfig,
383    pattern_counter: usize, // Counter for generating unique pattern names
384    imported_types: HashSet<String>, // Set of imported type names for lifetime tracking
385    types_with_lifetimes: HashSet<String>, // Set of type names that have been generated with <'a>
386    type_definitions: HashMap<String, Type>, // Map of type names to their definitions for inlining
387}
388
389impl CodeGenerator {
390    pub fn new() -> Self {
391        Self {
392            output: String::new(),
393            config: CodeGenConfig::default(),
394            pattern_counter: 0,
395            imported_types: HashSet::new(),
396            types_with_lifetimes: HashSet::new(),
397            type_definitions: HashMap::new(),
398        }
399    }
400
401    pub fn with_config(config: CodeGenConfig) -> Self {
402        Self {
403            output: String::new(),
404            config,
405            pattern_counter: 0,
406            imported_types: HashSet::new(),
407            types_with_lifetimes: HashSet::new(),
408            type_definitions: HashMap::new(),
409        }
410    }
411
412    /// Return the crate path for `TryFrom`.
413    ///
414    /// Always returns `"core"` — `core::convert::TryFrom` is available in both
415    /// `std` and `no_std` targets and is identical to `std::convert::TryFrom`.
416    /// The `use_core` config field is retained for API compatibility.
417    fn try_from_path(&self) -> &'static str {
418        "core"
419    }
420
421    /// Return the Cargo feature name used to gate derive annotations, or `None`
422    /// when [`DeriveMode::Always`] is active (no gating needed).
423    fn derive_feature_name(&self) -> Option<&str> {
424        match &self.config.derive_mode {
425            DeriveMode::Always => None,
426            DeriveMode::FeatureGated => Some("derive"),
427            DeriveMode::Custom(name) => Some(name.as_str()),
428        }
429    }
430
431    /// Format a top-level derive-gated attribute.
432    ///
433    /// - `Always` → `#[ATTR]`
434    /// - `FeatureGated` → `#[cfg_attr(feature = "derive", ATTR)]`
435    /// - `Custom(n)` → `#[cfg_attr(feature = "n", ATTR)]`
436    fn derive_cfg_attr(&self, attr: &str) -> String {
437        match self.derive_feature_name() {
438            None => format!("#[{}]", attr),
439            Some(feat) => format!("#[cfg_attr(feature = \"{}\", {})]", feat, attr),
440        }
441    }
442
443    /// Format a field-level (4-space-indented) derive-gated attribute.
444    fn field_derive_cfg_attr(&self, attr: &str) -> String {
445        match self.derive_feature_name() {
446            None => format!("    #[{}]", attr),
447            Some(feat) => format!("    #[cfg_attr(feature = \"{}\", {})]", feat, attr),
448        }
449    }
450
451    /// Check if module contains any PATTERN constraints
452    fn has_pattern_constraints(&self, module: &Module) -> bool {
453        for def in &module.definitions {
454            if self.type_has_pattern(&def.ty) {
455                return true;
456            }
457        }
458        false
459    }
460
461    /// Check if a type contains PATTERN constraints
462    fn type_has_pattern(&self, ty: &Type) -> bool {
463        match ty {
464            Type::Constrained {
465                constraint,
466                base_type,
467            } => {
468                if let ConstraintSpec::Subtype(subtype) = &constraint.spec {
469                    if self.constraint_has_pattern(subtype) {
470                        return true;
471                    }
472                }
473                self.type_has_pattern(base_type)
474            }
475            Type::Sequence(fields) | Type::Set(fields) => {
476                fields.iter().any(|f| self.type_has_pattern(&f.ty))
477            }
478            Type::Choice(variants) => variants.iter().any(|v| self.type_has_pattern(&v.ty)),
479            Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => self.type_has_pattern(inner),
480            Type::Tagged { inner, .. } => self.type_has_pattern(inner),
481            _ => false,
482        }
483    }
484
485    /// Check if a constraint contains PATTERN
486    fn constraint_has_pattern(&self, constraint: &SubtypeConstraint) -> bool {
487        match constraint {
488            SubtypeConstraint::Pattern(_) => true,
489            SubtypeConstraint::SizeConstraint(inner)
490            | SubtypeConstraint::InnerType(inner)
491            | SubtypeConstraint::Complement(inner) => self.constraint_has_pattern(inner),
492            SubtypeConstraint::Union(elements) | SubtypeConstraint::Intersection(elements) => {
493                elements.iter().any(|e| self.constraint_has_pattern(e))
494            }
495            _ => false,
496        }
497    }
498
499    /// Generate Rust code from a module
500    pub fn generate_module(&mut self, module: &Module) -> Result<String, std::fmt::Error> {
501        // File header
502        writeln!(
503            &mut self.output,
504            "// Auto-generated from ASN.1 module: {}",
505            module.name
506        )?;
507        writeln!(&mut self.output)?;
508
509        // Document exports if present
510        if !module.exports.is_empty() {
511            writeln!(&mut self.output, "// EXPORTS:")?;
512            for export in &module.exports {
513                writeln!(&mut self.output, "//   {}", export)?;
514            }
515            writeln!(&mut self.output)?;
516        }
517
518        // Document imports if present
519        if !module.imports.is_empty() {
520            writeln!(&mut self.output, "// IMPORTS:")?;
521            for import in &module.imports {
522                write!(&mut self.output, "//   ")?;
523                for (i, symbol) in import.symbols.iter().enumerate() {
524                    if i > 0 {
525                        write!(&mut self.output, ", ")?;
526                    }
527                    write!(&mut self.output, "{}", symbol)?;
528                }
529                writeln!(&mut self.output, " FROM {}", import.module_name)?;
530            }
531            writeln!(&mut self.output)?;
532        }
533
534        // Standard synta imports.  The #[allow(unused_imports)] attributes
535        // suppress clippy warnings for imports that are only needed when the
536        // `derive` feature is active or when specific ASN.1 constructs are used.
537        writeln!(&mut self.output, "#[allow(unused_imports)]")?;
538        writeln!(&mut self.output, "use synta::types::string::*;")?;
539        // All primitive, tagged, and constructed types are re-exported at the
540        // synta crate root.  Encoder / Decoder are needed for the Encode /
541        // Decode forwarding impls on constrained types.
542        writeln!(&mut self.output, "#[allow(unused_imports)]")?;
543        writeln!(
544            &mut self.output,
545            "use synta::{{Encode, Decode, Tagged, Encoder, Decoder, \
546             GeneralizedTime, ObjectIdentifier, UtcTime, \
547             Integer, Boolean, Enumerated, Null, Real, \
548             ExplicitTag, ImplicitTag, Element, RawDer, SetOf}};"
549        )?;
550        let derive_feat = self.derive_feature_name().map(str::to_owned);
551        if let Some(feat) = derive_feat {
552            writeln!(&mut self.output, "#[cfg(feature = \"{feat}\")]")?;
553        }
554        writeln!(&mut self.output, "#[allow(unused_imports)]")?;
555        writeln!(
556            &mut self.output,
557            "use synta_derive::{{Asn1Sequence, Asn1Choice, Asn1Set}};"
558        )?;
559
560        // Add regex imports if module contains PATTERN constraints
561        if self.has_pattern_constraints(module) {
562            writeln!(&mut self.output, "#[cfg(feature = \"regex\")]")?;
563            writeln!(&mut self.output, "use regex::Regex;")?;
564            writeln!(&mut self.output, "#[cfg(feature = \"regex\")]")?;
565            writeln!(&mut self.output, "use once_cell::sync::Lazy;")?;
566        }
567        writeln!(&mut self.output)?;
568
569        // Generate use statements for imported types if module_path_prefix is configured
570        if let Some(ref prefix) = self.config.module_path_prefix {
571            if !module.imports.is_empty() {
572                for import in &module.imports {
573                    let module_path = module_name_to_rust(&import.module_name);
574                    write!(&mut self.output, "use {}::{}", prefix, module_path)?;
575
576                    if import.symbols.len() == 1 {
577                        writeln!(&mut self.output, "::{};", import.symbols[0])?;
578                    } else {
579                        write!(&mut self.output, "::{{")?;
580                        for (i, symbol) in import.symbols.iter().enumerate() {
581                            if i > 0 {
582                                write!(&mut self.output, ", ")?;
583                            }
584                            write!(&mut self.output, "{}", symbol)?;
585                        }
586                        writeln!(&mut self.output, "}};")?;
587                    }
588                }
589                writeln!(&mut self.output)?;
590            }
591        }
592
593        // Build set of all imported type names (for lifetime tracking)
594        self.imported_types = module
595            .imports
596            .iter()
597            .flat_map(|import| import.symbols.iter())
598            .cloned()
599            .collect();
600
601        // Generate value assignments (constants)
602        if !module.values.is_empty() {
603            writeln!(
604                &mut self.output,
605                "// ============================================================================"
606            )?;
607            writeln!(&mut self.output, "// Constants")?;
608            writeln!(
609                &mut self.output,
610                "// ============================================================================\n"
611            )?;
612
613            // Build OID registry for resolving named references
614            let oid_registry = self.build_oid_registry(&module.values);
615
616            for value_assignment in &module.values {
617                self.generate_value_assignment(value_assignment, &oid_registry)?;
618            }
619            writeln!(&mut self.output)?;
620        }
621
622        // PASS 0: Build type definitions map for inlining
623        for def in &module.definitions {
624            let type_name = to_pascal_case(&def.name);
625            self.type_definitions.insert(type_name, def.ty.clone());
626        }
627
628        // PASS 1: Pre-scan all definitions to determine which types need lifetimes
629        // This is needed to handle forward references
630        self.prescan_types_for_lifetimes(&module.definitions);
631
632        // PASS 2: Generate each definition, skipping types that are in skip_imported_types
633        for def in &module.definitions {
634            // Skip this definition if it's in the skip list (external import)
635            if self.config.skip_imported_types.contains(&def.name) {
636                continue;
637            }
638
639            self.generate_definition(def)?;
640            writeln!(&mut self.output)?;
641        }
642
643        Ok(self.output.clone())
644    }
645
646    fn generate_definition(&mut self, def: &Definition) -> Result<(), std::fmt::Error> {
647        let type_name = to_pascal_case(&def.name);
648
649        match &def.ty {
650            Type::Sequence(fields) => {
651                self.generate_sequence_type(&type_name, fields)?;
652            }
653            Type::Set(fields) => {
654                self.generate_set_type(&type_name, fields)?;
655            }
656            Type::Choice(variants) => {
657                self.generate_choice_type(&type_name, variants)?;
658            }
659            Type::SequenceOf(inner, size_constraint) => {
660                self.generate_sequence_of_type(&type_name, inner, size_constraint.as_ref())?;
661            }
662            Type::SetOf(inner, size_constraint) => {
663                self.generate_set_of_type(&type_name, inner, size_constraint.as_ref())?;
664            }
665            // X.680 constrained types - generate validated newtypes
666            Type::Constrained {
667                base_type,
668                constraint,
669            } => {
670                match (base_type.as_ref(), &constraint.spec) {
671                    (
672                        Type::Integer(_, named_numbers),
673                        ConstraintSpec::Subtype(subtype_constraint),
674                    ) => {
675                        self.generate_constrained_integer(&type_name, subtype_constraint)?;
676
677                        // Generate named constants if present
678                        if !named_numbers.is_empty() {
679                            let prim = Self::constrained_integer_rust_type(subtype_constraint);
680                            writeln!(&mut self.output)?;
681                            writeln!(&mut self.output, "impl {} {{", type_name)?;
682
683                            for named_number in named_numbers {
684                                let const_name = to_screaming_snake_case(&named_number.name);
685                                writeln!(
686                                    &mut self.output,
687                                    "    pub const {}: {} = {};",
688                                    const_name, prim, named_number.value
689                                )?;
690                            }
691
692                            writeln!(&mut self.output, "}}")?;
693                        }
694                    }
695                    (Type::IA5String(_), ConstraintSpec::Subtype(subtype_constraint)) => {
696                        self.generate_constrained_string(
697                            &type_name,
698                            "IA5String",
699                            subtype_constraint,
700                        )?;
701                    }
702                    (Type::PrintableString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
703                        self.generate_constrained_string(
704                            &type_name,
705                            "PrintableString",
706                            subtype_constraint,
707                        )?;
708                    }
709                    (Type::Utf8String(_), ConstraintSpec::Subtype(subtype_constraint)) => {
710                        self.generate_constrained_string(
711                            &type_name,
712                            "Utf8String",
713                            subtype_constraint,
714                        )?;
715                    }
716                    (Type::TeletexString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
717                        self.generate_constrained_string(
718                            &type_name,
719                            "TeletexString",
720                            subtype_constraint,
721                        )?;
722                    }
723                    (Type::UniversalString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
724                        self.generate_constrained_string(
725                            &type_name,
726                            "UniversalString",
727                            subtype_constraint,
728                        )?;
729                    }
730                    (Type::BmpString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
731                        self.generate_constrained_string(
732                            &type_name,
733                            "BmpString",
734                            subtype_constraint,
735                        )?;
736                    }
737                    (Type::GeneralString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
738                        self.generate_constrained_string(
739                            &type_name,
740                            "GeneralString",
741                            subtype_constraint,
742                        )?;
743                    }
744                    (Type::NumericString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
745                        self.generate_constrained_string(
746                            &type_name,
747                            "NumericString",
748                            subtype_constraint,
749                        )?;
750                    }
751                    (Type::VisibleString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
752                        self.generate_constrained_string(
753                            &type_name,
754                            "VisibleString",
755                            subtype_constraint,
756                        )?;
757                    }
758                    (Type::OctetString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
759                        self.generate_constrained_string(
760                            &type_name,
761                            "OctetString",
762                            subtype_constraint,
763                        )?;
764                    }
765                    (
766                        Type::BitString(_),
767                        ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(named_bits)),
768                    ) => {
769                        // Named bit list without size constraint: type alias + bit constants
770                        writeln!(&mut self.output, "pub type {} = BitString;", type_name)?;
771                        self.generate_named_bit_constants(&type_name, named_bits)?;
772                    }
773                    (
774                        Type::BitString(_),
775                        ConstraintSpec::Subtype(SubtypeConstraint::Intersection(constraints)),
776                    ) if constraints
777                        .iter()
778                        .any(|c| matches!(c, SubtypeConstraint::NamedBitList(_))) =>
779                    {
780                        // Named bit list with size constraint: constrained newtype + bit constants
781                        let size_con = constraints
782                            .iter()
783                            .find(|c| matches!(c, SubtypeConstraint::SizeConstraint(_)));
784                        let named_bits_opt = constraints.iter().find_map(|c| {
785                            if let SubtypeConstraint::NamedBitList(bits) = c {
786                                Some(bits)
787                            } else {
788                                None
789                            }
790                        });
791
792                        if let Some(size_con) = size_con {
793                            self.generate_constrained_string(&type_name, "BitString", size_con)?;
794                        } else {
795                            writeln!(&mut self.output, "pub type {} = BitString;", type_name)?;
796                        }
797
798                        if let Some(named_bits) = named_bits_opt {
799                            self.generate_named_bit_constants(&type_name, named_bits)?;
800                        }
801                    }
802                    (Type::BitString(_), ConstraintSpec::Subtype(subtype_constraint)) => {
803                        self.generate_constrained_string(
804                            &type_name,
805                            "BitString",
806                            subtype_constraint,
807                        )?;
808                    }
809                    (Type::TypeRef(_), ConstraintSpec::Subtype(subtype_constraint)) => {
810                        // Subtype definition: NewType ::= BaseType (constraint)
811                        self.generate_subtype(&type_name, base_type, subtype_constraint)?;
812                    }
813                    _ => {
814                        // For unsupported constraint types, fall back to type alias with comment
815                        writeln!(
816                            &mut self.output,
817                            "// Constrained type (validation not yet implemented)"
818                        )?;
819                        let rust_type = self.rust_type(base_type);
820                        self.generate_type_alias(&type_name, &rust_type, base_type)?;
821                    }
822                }
823            }
824            Type::Integer(_, named_numbers) if !named_numbers.is_empty() => {
825                // INTEGER with named values - generate type alias and module-level constants.
826                // Module-level consts avoid the orphan-rule restriction on impl blocks for
827                // type aliases of foreign types, and allow `const` without Integer::from().
828                writeln!(&mut self.output, "pub type {} = Integer;", type_name)?;
829                self.generate_named_integer_constants(&type_name, named_numbers)?;
830            }
831            Type::Integer(Some(constraint), _) => {
832                // Legacy constraint - type alias with constraint comment
833                let constraint_str = self.format_value_constraint(constraint);
834                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
835                writeln!(&mut self.output, "pub type {} = Integer;", type_name)?;
836            }
837            Type::OctetString(Some(constraint))
838            | Type::BitString(Some(constraint))
839            | Type::Utf8String(Some(constraint))
840            | Type::PrintableString(Some(constraint))
841            | Type::IA5String(Some(constraint))
842            | Type::TeletexString(Some(constraint))
843            | Type::UniversalString(Some(constraint))
844            | Type::BmpString(Some(constraint))
845            | Type::GeneralString(Some(constraint))
846            | Type::NumericString(Some(constraint))
847            | Type::VisibleString(Some(constraint)) => {
848                // Legacy constraint - type alias with size constraint comment
849                let constraint_str = self.format_size_constraint(constraint);
850                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
851                let rust_type = self.rust_type(&def.ty);
852                self.generate_type_alias(&type_name, &rust_type, &def.ty)?;
853            }
854            Type::Enumerated(named_values) => {
855                self.generate_enumerated_type(&type_name, named_values)?;
856            }
857            Type::Tagged { tag, inner } => {
858                // Top-level tagged type: emit a doc comment then generate the inner type
859                let class_str = match tag.class {
860                    TagClass::Application => "APPLICATION",
861                    TagClass::Universal => "UNIVERSAL",
862                    TagClass::Private => "PRIVATE",
863                    TagClass::ContextSpecific => "CONTEXT",
864                };
865                let tagging_str = match tag.tagging {
866                    Tagging::Explicit => "EXPLICIT",
867                    Tagging::Implicit => "IMPLICIT",
868                };
869                writeln!(
870                    &mut self.output,
871                    "/// [{} {}] {} outer tag",
872                    class_str, tag.number, tagging_str
873                )?;
874                // Generate the inner type under the same name
875                let inner_def = Definition {
876                    name: def.name.clone(),
877                    ty: *inner.clone(),
878                };
879                self.generate_definition(&inner_def)?;
880            }
881            Type::Class(fields) => {
882                // ASN.1 Information Object Classes (X.681 §9) have no DER encoding.
883                // Emit a structured comment block so the schema is self-documenting
884                // but do not generate any Rust type.
885                writeln!(
886                    &mut self.output,
887                    "// ASN.1 Information Object Class: {} (no Rust type generated)",
888                    type_name
889                )?;
890                if !fields.is_empty() {
891                    write!(&mut self.output, "// Fields:")?;
892                    for field in fields {
893                        write!(&mut self.output, " &{}", field.name)?;
894                        if field.unique {
895                            write!(&mut self.output, " UNIQUE")?;
896                        }
897                        if field.optional {
898                            write!(&mut self.output, " OPTIONAL")?;
899                        }
900                        write!(&mut self.output, ";")?;
901                    }
902                    writeln!(&mut self.output)?;
903                }
904            }
905            _ => {
906                // Type alias
907                let rust_type = self.rust_type(&def.ty);
908                self.generate_type_alias(&type_name, &rust_type, &def.ty)?;
909            }
910        }
911
912        Ok(())
913    }
914
915    fn format_value_constraint(&self, constraint: &ValueConstraint) -> String {
916        match constraint {
917            ValueConstraint::Single(val) => format!("value = {}", val),
918            ValueConstraint::Range(min, max) => {
919                let min_str = min
920                    .map(|v| v.to_string())
921                    .unwrap_or_else(|| "MIN".to_string());
922                let max_str = max
923                    .map(|v| v.to_string())
924                    .unwrap_or_else(|| "MAX".to_string());
925                format!("{}..{}", min_str, max_str)
926            }
927        }
928    }
929
930    fn format_size_constraint(&self, constraint: &SizeConstraint) -> String {
931        match constraint {
932            SizeConstraint::Fixed(size) => format!("SIZE ({})", size),
933            SizeConstraint::Range(min, max) => {
934                let min_str = min
935                    .map(|v| v.to_string())
936                    .unwrap_or_else(|| "0".to_string());
937                let max_str = max
938                    .map(|v| v.to_string())
939                    .unwrap_or_else(|| "MAX".to_string());
940                format!("SIZE ({}..{})", min_str, max_str)
941            }
942        }
943    }
944
945    /// Return the smallest Rust primitive integer type whose range covers all
946    /// values permitted by `constraint`.
947    ///
948    /// When the lower bound is ≥ 0 (non-negative), unsigned types are preferred
949    /// because they cover twice the range of the same-width signed type:
950    /// `u8` (0..=255), `u16` (0..=65535), `u32` (0..=4294967295), `u64`.
951    ///
952    /// When the lower bound is negative, the smallest signed type that fits both
953    /// bounds is chosen: `i8`, `i16`, `i32`, `i64`.
954    ///
955    /// Falls back to `i64` / `u64` when either bound is `MIN`, `MAX`, a named
956    /// value (not a literal), or the constraint is not a simple value/range.
957    fn constrained_integer_rust_type(constraint: &SubtypeConstraint) -> &'static str {
958        let (lo, hi) = match constraint {
959            SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) => (*n, *n),
960            SubtypeConstraint::ValueRange {
961                min: ConstraintValue::Integer(lo),
962                max: ConstraintValue::Integer(hi),
963            } => (*lo, *hi),
964            _ => return "i64",
965        };
966        if lo >= 0 {
967            // Non-negative range — use the smallest unsigned type.
968            if hi <= u8::MAX as i64 {
969                "u8"
970            } else if hi <= u16::MAX as i64 {
971                "u16"
972            } else if hi <= u32::MAX as i64 {
973                "u32"
974            } else {
975                "u64"
976            }
977        } else {
978            // Range includes negative values — use signed types.
979            if lo >= i8::MIN as i64 && hi <= i8::MAX as i64 {
980                "i8"
981            } else if lo >= i16::MIN as i64 && hi <= i16::MAX as i64 {
982                "i16"
983            } else if lo >= i32::MIN as i64 && hi <= i32::MAX as i64 {
984                "i32"
985            } else {
986                "i64"
987            }
988        }
989    }
990
991    /// Generate validation code for a constraint value
992    fn generate_constraint_value_check(&self, var: &str, value: &ConstraintValue) -> String {
993        match value {
994            ConstraintValue::Integer(n) => format!("{} == {}", var, n),
995            ConstraintValue::Min => "true /* MIN */".to_string(),
996            ConstraintValue::Max => "true /* MAX */".to_string(),
997            ConstraintValue::NamedValue(name) => format!("{} == {} /* named value */", var, name),
998        }
999    }
1000
1001    /// Generate validation code for a subtype constraint
1002    fn generate_constraint_validation(&self, var: &str, constraint: &SubtypeConstraint) -> String {
1003        match constraint {
1004            SubtypeConstraint::SingleValue(val) => self.generate_constraint_value_check(var, val),
1005            SubtypeConstraint::ValueRange { min, max } => {
1006                // Use RangeInclusive::contains when both bounds are concrete integers
1007                // to avoid clippy's manual_range_contains warning.
1008                if let (ConstraintValue::Integer(lo), ConstraintValue::Integer(hi)) = (min, max) {
1009                    return format!("({}..={}).contains(&{})", lo, hi, var);
1010                }
1011                let mut parts: Vec<String> = Vec::new();
1012                match min {
1013                    ConstraintValue::Integer(n) => parts.push(format!("{} >= {}", var, n)),
1014                    ConstraintValue::Min => {}
1015                    ConstraintValue::NamedValue(name) => parts.push(format!("{} >= {}", var, name)),
1016                    ConstraintValue::Max => parts.push(format!("{} <= i64::MAX", var)),
1017                }
1018                match max {
1019                    ConstraintValue::Integer(n) => parts.push(format!("{} <= {}", var, n)),
1020                    ConstraintValue::Max => {}
1021                    ConstraintValue::NamedValue(name) => parts.push(format!("{} <= {}", var, name)),
1022                    ConstraintValue::Min => parts.push(format!("{} >= i64::MIN", var)),
1023                }
1024                if parts.is_empty() {
1025                    "true".to_string()
1026                } else {
1027                    format!("({})", parts.join(" && "))
1028                }
1029            }
1030            SubtypeConstraint::Union(elements) => {
1031                let checks: Vec<String> = elements
1032                    .iter()
1033                    .map(|e| self.generate_constraint_validation(var, e))
1034                    .collect();
1035                format!("({})", checks.join(" || "))
1036            }
1037            SubtypeConstraint::Intersection(elements) => {
1038                let checks: Vec<String> = elements
1039                    .iter()
1040                    .map(|e| self.generate_constraint_validation(var, e))
1041                    .collect();
1042                format!("({})", checks.join(" && "))
1043            }
1044            SubtypeConstraint::Complement(inner) => {
1045                let inner_check = self.generate_constraint_validation(var, inner);
1046                format!("!({})", inner_check)
1047            }
1048            _ => "true /* unsupported constraint */".to_string(),
1049        }
1050    }
1051
1052    /// Generate a human-readable constraint description for error messages
1053    fn generate_constraint_description(&self, constraint: &SubtypeConstraint) -> String {
1054        match constraint {
1055            SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) => {
1056                format!("must equal {}", n)
1057            }
1058            SubtypeConstraint::ValueRange { min, max } => {
1059                let min_str = match min {
1060                    ConstraintValue::Integer(n) => n.to_string(),
1061                    ConstraintValue::Min => "MIN".to_string(),
1062                    ConstraintValue::NamedValue(n) => n.clone(),
1063                    ConstraintValue::Max => "MAX".to_string(),
1064                };
1065                let max_str = match max {
1066                    ConstraintValue::Integer(n) => n.to_string(),
1067                    ConstraintValue::Max => "MAX".to_string(),
1068                    ConstraintValue::NamedValue(n) => n.clone(),
1069                    ConstraintValue::Min => "MIN".to_string(),
1070                };
1071                format!("must be in range {}..{}", min_str, max_str)
1072            }
1073            SubtypeConstraint::Union(elements) => {
1074                let descriptions: Vec<String> = elements
1075                    .iter()
1076                    .map(|e| self.generate_constraint_description(e))
1077                    .collect();
1078                format!("must satisfy one of: {}", descriptions.join(", "))
1079            }
1080            SubtypeConstraint::Complement(inner) => {
1081                format!(
1082                    "must not be {}",
1083                    self.generate_constraint_description(inner)
1084                )
1085            }
1086            _ => "must satisfy constraint".to_string(),
1087        }
1088    }
1089
1090    /// Generate a proper Rust enum for an ENUMERATED type
1091    fn generate_enumerated_type(
1092        &mut self,
1093        name: &str,
1094        named_values: &[NamedNumber],
1095    ) -> Result<(), std::fmt::Error> {
1096        writeln!(&mut self.output, "/// ENUMERATED")?;
1097        writeln!(
1098            &mut self.output,
1099            "#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]"
1100        )?;
1101        writeln!(&mut self.output, "#[repr(i64)]")?;
1102        writeln!(&mut self.output, "pub enum {} {{", name)?;
1103
1104        for nv in named_values {
1105            let variant_name = to_pascal_case(&nv.name);
1106            writeln!(&mut self.output, "    {} = {},", variant_name, nv.value)?;
1107        }
1108
1109        writeln!(&mut self.output, "}}")?;
1110        writeln!(&mut self.output)?;
1111
1112        // TryFrom<Integer> implementation
1113        let try_from_path = self.try_from_path();
1114        writeln!(
1115            &mut self.output,
1116            "impl {}::convert::TryFrom<Integer> for {} {{",
1117            try_from_path, name
1118        )?;
1119        writeln!(&mut self.output, "    type Error = &'static str;")?;
1120        writeln!(&mut self.output)?;
1121        writeln!(
1122            &mut self.output,
1123            "    fn try_from(value: Integer) -> Result<Self, Self::Error> {{"
1124        )?;
1125        writeln!(
1126            &mut self.output,
1127            "        let discriminant = value.as_i64().map_err(|_| \"ENUMERATED value out of i64 range\")?;"
1128        )?;
1129        writeln!(&mut self.output, "        match discriminant {{")?;
1130
1131        for nv in named_values {
1132            let variant_name = to_pascal_case(&nv.name);
1133            writeln!(
1134                &mut self.output,
1135                "            {} => Ok({}::{}),",
1136                nv.value, name, variant_name
1137            )?;
1138        }
1139
1140        writeln!(
1141            &mut self.output,
1142            "            _ => Err(\"unknown ENUMERATED value\"),"
1143        )?;
1144        writeln!(&mut self.output, "        }}")?;
1145        writeln!(&mut self.output, "    }}")?;
1146        writeln!(&mut self.output, "}}")?;
1147        writeln!(&mut self.output)?;
1148
1149        // From<T> for Integer
1150        writeln!(&mut self.output, "impl From<{}> for Integer {{", name)?;
1151        writeln!(&mut self.output, "    fn from(value: {}) -> Self {{", name)?;
1152        writeln!(&mut self.output, "        Integer::from(value as i64)")?;
1153        writeln!(&mut self.output, "    }}")?;
1154        writeln!(&mut self.output, "}}")?;
1155        writeln!(&mut self.output)?;
1156
1157        // Encode / Decode / Tagged forwarding impls.
1158        // ENUMERATED (tag 10) is encoded identically to INTEGER (tag 2) but
1159        // with a different universal tag number.
1160        writeln!(&mut self.output, "impl Encode for {} {{", name)?;
1161        writeln!(
1162            &mut self.output,
1163            "    fn encode(&self, encoder: &mut Encoder) -> synta::Result<()> {{"
1164        )?;
1165        writeln!(
1166            &mut self.output,
1167            "        let as_int = Integer::from(*self as i64);"
1168        )?;
1169        writeln!(
1170            &mut self.output,
1171            "        let tag = synta::Tag::universal(synta::tag::TAG_ENUMERATED);"
1172        )?;
1173        writeln!(&mut self.output, "        encoder.write_tag(tag)?;")?;
1174        writeln!(
1175            &mut self.output,
1176            "        encoder.write_length(as_int.as_bytes().len())?;"
1177        )?;
1178        writeln!(
1179            &mut self.output,
1180            "        encoder.write_bytes(as_int.as_bytes());"
1181        )?;
1182        writeln!(&mut self.output, "        Ok(())")?;
1183        writeln!(&mut self.output, "    }}")?;
1184        writeln!(
1185            &mut self.output,
1186            "    fn encoded_len(&self) -> synta::Result<usize> {{"
1187        )?;
1188        writeln!(
1189            &mut self.output,
1190            "        let as_int = Integer::from(*self as i64);"
1191        )?;
1192        writeln!(&mut self.output, "        let tag_len = 1usize;")?;
1193        writeln!(
1194            &mut self.output,
1195            "        let length = as_int.as_bytes().len();"
1196        )?;
1197        writeln!(
1198            &mut self.output,
1199            "        let length_len = synta::Length::Definite(length).encoded_len()?;"
1200        )?;
1201        writeln!(
1202            &mut self.output,
1203            "        Ok(tag_len + length_len + length)"
1204        )?;
1205        writeln!(&mut self.output, "    }}")?;
1206        writeln!(&mut self.output, "}}")?;
1207        writeln!(&mut self.output)?;
1208
1209        writeln!(&mut self.output, "impl<'a> Decode<'a> for {} {{", name)?;
1210        writeln!(
1211            &mut self.output,
1212            "    fn decode(decoder: &mut Decoder<'a>) -> synta::Result<Self> {{"
1213        )?;
1214        writeln!(&mut self.output, "        let tag = decoder.read_tag()?;")?;
1215        writeln!(
1216            &mut self.output,
1217            "        let expected = synta::Tag::universal(synta::tag::TAG_ENUMERATED);"
1218        )?;
1219        writeln!(&mut self.output, "        if tag != expected {{")?;
1220        writeln!(
1221            &mut self.output,
1222            "            return Err(synta::Error::UnexpectedTag {{"
1223        )?;
1224        writeln!(
1225            &mut self.output,
1226            "                position: decoder.position(),"
1227        )?;
1228        writeln!(&mut self.output, "                expected,")?;
1229        writeln!(&mut self.output, "                actual: tag,")?;
1230        writeln!(&mut self.output, "            }});")?;
1231        writeln!(&mut self.output, "        }}")?;
1232        writeln!(
1233            &mut self.output,
1234            "        let length = decoder.read_length()?;"
1235        )?;
1236        writeln!(&mut self.output, "        let len = length.definite()?;")?;
1237        writeln!(
1238            &mut self.output,
1239            "        let bytes = decoder.read_bytes(len)?;"
1240        )?;
1241        writeln!(
1242            &mut self.output,
1243            "        let integer = Integer::from_bytes(bytes);"
1244        )?;
1245        writeln!(
1246            &mut self.output,
1247            "        core::convert::TryFrom::try_from(integer)\
1248             .map_err(|_| synta::Error::LengthOverflow)"
1249        )?;
1250        writeln!(&mut self.output, "    }}")?;
1251        writeln!(&mut self.output, "}}")?;
1252        writeln!(&mut self.output)?;
1253
1254        writeln!(&mut self.output, "impl Tagged for {} {{", name)?;
1255        writeln!(
1256            &mut self.output,
1257            "    fn tag() -> synta::Tag {{ synta::Tag::universal(synta::tag::TAG_ENUMERATED) }}"
1258        )?;
1259        writeln!(&mut self.output, "}}")?;
1260
1261        self.generate_format_asn1_impl(name, false)?;
1262
1263        Ok(())
1264    }
1265
1266    /// Generate named bit position constants for a BIT STRING type
1267    fn generate_named_bit_constants(
1268        &mut self,
1269        name: &str,
1270        named_bits: &[NamedNumber],
1271    ) -> Result<(), std::fmt::Error> {
1272        if named_bits.is_empty() {
1273            return Ok(());
1274        }
1275
1276        writeln!(&mut self.output)?;
1277        writeln!(
1278            &mut self.output,
1279            "// Named bit positions for {} (defined as module-level constants to avoid orphan rule issues)",
1280            name
1281        )?;
1282
1283        for bit in named_bits {
1284            let const_name = to_screaming_snake_case(&bit.name);
1285            let full_const_name = format!("{}_{}", to_screaming_snake_case(name), const_name);
1286            writeln!(
1287                &mut self.output,
1288                "/// Bit position for `{}` in {}",
1289                bit.name, name
1290            )?;
1291            writeln!(
1292                &mut self.output,
1293                "pub const {}: usize = {};",
1294                full_const_name, bit.value
1295            )?;
1296        }
1297
1298        Ok(())
1299    }
1300
1301    /// Generate module-level named-value constants for an unconstrained INTEGER type.
1302    ///
1303    /// Uses `i64` literals rather than `Integer::from()` (which is not `const`)
1304    /// and avoids `impl TypeAlias { … }` which the orphan rule disallows for
1305    /// type aliases of foreign types.
1306    fn generate_named_integer_constants(
1307        &mut self,
1308        name: &str,
1309        named_numbers: &[NamedNumber],
1310    ) -> Result<(), std::fmt::Error> {
1311        if named_numbers.is_empty() {
1312            return Ok(());
1313        }
1314
1315        writeln!(&mut self.output)?;
1316        writeln!(
1317            &mut self.output,
1318            "// Named values for {} (defined as module-level constants to avoid orphan rule issues)",
1319            name
1320        )?;
1321
1322        for num in named_numbers {
1323            let const_name = to_screaming_snake_case(&num.name);
1324            let full_const_name = format!("{}_{}", to_screaming_snake_case(name), const_name);
1325            writeln!(
1326                &mut self.output,
1327                "/// Named value `{}` for {}",
1328                num.name, name
1329            )?;
1330            writeln!(
1331                &mut self.output,
1332                "pub const {}: i64 = {};",
1333                full_const_name, num.value
1334            )?;
1335        }
1336
1337        Ok(())
1338    }
1339
1340    /// Generate a validated newtype for a constrained INTEGER.
1341    ///
1342    /// Picks the smallest Rust primitive integer that fits the constraint range.
1343    /// When the lower bound is ≥ 0, an unsigned type is chosen (`u8`, `u16`,
1344    /// `u32`, `u64`); when the lower bound is negative, a signed type is chosen
1345    /// (`i8`, `i16`, `i32`, `i64`).  Unconstrained bounds fall back to `i64`.
1346    /// Using a native primitive gives the generated struct `Copy`, `PartialOrd`,
1347    /// and `Ord` for free and avoids the heap allocation that the
1348    /// arbitrary-precision `Integer` type would incur.
1349    fn generate_constrained_integer(
1350        &mut self,
1351        name: &str,
1352        constraint: &SubtypeConstraint,
1353    ) -> Result<(), std::fmt::Error> {
1354        let prim = Self::constrained_integer_rust_type(constraint);
1355
1356        // Generate documentation with constraint info
1357        let constraint_display = self.format_constraint_display(constraint);
1358        writeln!(&mut self.output, "/// INTEGER ({})", constraint_display)?;
1359        writeln!(
1360            &mut self.output,
1361            "#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]"
1362        )?;
1363        writeln!(&mut self.output, "pub struct {}({});", name, prim)?;
1364        writeln!(&mut self.output)?;
1365
1366        // Generate constructor with validation
1367        writeln!(&mut self.output, "impl {} {{", name)?;
1368        writeln!(
1369            &mut self.output,
1370            "    /// Create a new {} with validation",
1371            name
1372        )?;
1373        writeln!(
1374            &mut self.output,
1375            "    pub fn new(value: {}) -> Result<Self, &'static str> {{",
1376            prim
1377        )?;
1378
1379        let validation = self.generate_constraint_validation("value", constraint);
1380        let description = self.generate_constraint_description(constraint);
1381
1382        writeln!(&mut self.output, "        if {} {{", validation)?;
1383        writeln!(&mut self.output, "            Ok({}(value))", name)?;
1384        writeln!(&mut self.output, "        }} else {{")?;
1385        writeln!(&mut self.output, "            Err(\"{}\")", description)?;
1386        writeln!(&mut self.output, "        }}")?;
1387        writeln!(&mut self.output, "    }}")?;
1388        writeln!(&mut self.output)?;
1389
1390        // Unchecked constructor for when you know the value is valid
1391        writeln!(
1392            &mut self.output,
1393            "    /// Create without validation (use with caution)"
1394        )?;
1395        writeln!(
1396            &mut self.output,
1397            "    pub const fn new_unchecked(value: {}) -> Self {{",
1398            prim
1399        )?;
1400        writeln!(&mut self.output, "        {}(value)", name)?;
1401        writeln!(&mut self.output, "    }}")?;
1402        writeln!(&mut self.output)?;
1403
1404        // Getter — returns by value since primitives are Copy
1405        writeln!(&mut self.output, "    /// Get the inner value")?;
1406        writeln!(
1407            &mut self.output,
1408            "    pub const fn get(&self) -> {} {{",
1409            prim
1410        )?;
1411        writeln!(&mut self.output, "        self.0")?;
1412        writeln!(&mut self.output, "    }}")?;
1413        writeln!(&mut self.output)?;
1414
1415        // Into inner
1416        writeln!(
1417            &mut self.output,
1418            "    /// Consume and return the inner value"
1419        )?;
1420        writeln!(
1421            &mut self.output,
1422            "    pub fn into_inner(self) -> {} {{",
1423            prim
1424        )?;
1425        writeln!(&mut self.output, "        self.0")?;
1426        writeln!(&mut self.output, "    }}")?;
1427        writeln!(&mut self.output, "}}")?;
1428        writeln!(&mut self.output)?;
1429
1430        // TryFrom<Integer> — decode path: wire Integer → native primitive → validated newtype.
1431        // Use the native as_{prim}() method: a single fallible step that handles both
1432        // the i64/u64 sign check and the narrowing conversion in one call.
1433        let as_method = format!("as_{}", prim);
1434        let try_from_path = self.try_from_path();
1435        writeln!(
1436            &mut self.output,
1437            "impl {}::convert::TryFrom<Integer> for {} {{",
1438            try_from_path, name
1439        )?;
1440        writeln!(&mut self.output, "    type Error = &'static str;")?;
1441        writeln!(&mut self.output)?;
1442        writeln!(
1443            &mut self.output,
1444            "    fn try_from(value: Integer) -> Result<Self, Self::Error> {{"
1445        )?;
1446        writeln!(
1447            &mut self.output,
1448            "        let n = value.{as_method}().map_err(|_| \"integer value out of {prim} range\")?;"
1449        )?;
1450        writeln!(&mut self.output, "        Self::new(n)")?;
1451        writeln!(&mut self.output, "    }}")?;
1452        writeln!(&mut self.output, "}}")?;
1453        writeln!(&mut self.output)?;
1454
1455        // Encode / Decode / Tagged forwarding impls — required so that this
1456        // type can appear as a field in #[derive(Asn1Sequence)] structs.
1457        // Encode: Integer::from(self.0) — uses the From<{prim}> impl directly.
1458        writeln!(&mut self.output, "impl Encode for {} {{", name)?;
1459        writeln!(
1460            &mut self.output,
1461            "    fn encode(&self, encoder: &mut Encoder) -> synta::Result<()> {{"
1462        )?;
1463        writeln!(
1464            &mut self.output,
1465            "        Integer::from(self.0).encode(encoder)"
1466        )?;
1467        writeln!(&mut self.output, "    }}")?;
1468        writeln!(
1469            &mut self.output,
1470            "    fn encoded_len(&self) -> synta::Result<usize> {{"
1471        )?;
1472        writeln!(
1473            &mut self.output,
1474            "        Integer::from(self.0).encoded_len()"
1475        )?;
1476        writeln!(&mut self.output, "    }}")?;
1477        writeln!(&mut self.output, "}}")?;
1478        writeln!(&mut self.output)?;
1479
1480        // Decode: wire Integer → as_{prim}() → new() — single narrowing step.
1481        writeln!(&mut self.output, "impl<'a> Decode<'a> for {} {{", name)?;
1482        writeln!(
1483            &mut self.output,
1484            "    fn decode(decoder: &mut Decoder<'a>) -> synta::Result<Self> {{"
1485        )?;
1486        writeln!(
1487            &mut self.output,
1488            "        Integer::decode(decoder).and_then(|v| {{"
1489        )?;
1490        writeln!(
1491            &mut self.output,
1492            "            let n = v.{as_method}().map_err(|_| synta::Error::LengthOverflow)?;"
1493        )?;
1494        writeln!(
1495            &mut self.output,
1496            "            Self::new(n).map_err(|_| synta::Error::LengthOverflow)"
1497        )?;
1498        writeln!(&mut self.output, "        }})")?;
1499        writeln!(&mut self.output, "    }}")?;
1500        writeln!(&mut self.output, "}}")?;
1501        writeln!(&mut self.output)?;
1502
1503        writeln!(&mut self.output, "impl Tagged for {} {{", name)?;
1504        writeln!(
1505            &mut self.output,
1506            "    fn tag() -> synta::Tag {{ Integer::tag() }}"
1507        )?;
1508        writeln!(&mut self.output, "}}")?;
1509
1510        self.generate_format_asn1_impl(name, false)?;
1511
1512        Ok(())
1513    }
1514
1515    /// Generate a validated newtype for a constrained string type
1516    fn generate_constrained_string(
1517        &mut self,
1518        name: &str,
1519        base_type: &str,
1520        constraint: &SubtypeConstraint,
1521    ) -> Result<(), std::fmt::Error> {
1522        // Generate documentation with constraint info
1523        let constraint_display = self.format_constraint_display(constraint);
1524        writeln!(
1525            &mut self.output,
1526            "/// {} ({})",
1527            base_type, constraint_display
1528        )?;
1529        writeln!(&mut self.output, "#[derive(Debug, Clone, PartialEq, Eq)]")?;
1530        writeln!(&mut self.output, "pub struct {}({});", name, base_type)?;
1531        writeln!(&mut self.output)?;
1532
1533        // Generate constructor with validation
1534        writeln!(&mut self.output, "impl {} {{", name)?;
1535        writeln!(
1536            &mut self.output,
1537            "    /// Create a new {} with validation",
1538            name
1539        )?;
1540        writeln!(
1541            &mut self.output,
1542            "    pub fn new(value: {}) -> Result<Self, &'static str> {{",
1543            base_type
1544        )?;
1545
1546        // Generate validation based on constraint type
1547        let validation_code = self.generate_string_validation("value", base_type, constraint);
1548        writeln!(&mut self.output, "{}", validation_code)?;
1549
1550        writeln!(&mut self.output, "    }}")?;
1551        writeln!(&mut self.output)?;
1552
1553        // Unchecked constructor
1554        writeln!(
1555            &mut self.output,
1556            "    /// Create without validation (use with caution)"
1557        )?;
1558        writeln!(
1559            &mut self.output,
1560            "    pub fn new_unchecked(value: {}) -> Self {{",
1561            base_type
1562        )?;
1563        writeln!(&mut self.output, "        {}(value)", name)?;
1564        writeln!(&mut self.output, "    }}")?;
1565        writeln!(&mut self.output)?;
1566
1567        // Getter
1568        writeln!(
1569            &mut self.output,
1570            "    /// Get a reference to the inner value"
1571        )?;
1572        writeln!(
1573            &mut self.output,
1574            "    pub fn get(&self) -> &{} {{",
1575            base_type
1576        )?;
1577        writeln!(&mut self.output, "        &self.0")?;
1578        writeln!(&mut self.output, "    }}")?;
1579        writeln!(&mut self.output)?;
1580
1581        // As str for string types
1582        if base_type != "OctetString" && base_type != "BitString" {
1583            writeln!(&mut self.output, "    /// Get the string value")?;
1584            writeln!(&mut self.output, "    pub fn as_str(&self) -> &str {{")?;
1585            writeln!(&mut self.output, "        self.0.as_str()")?;
1586            writeln!(&mut self.output, "    }}")?;
1587            writeln!(&mut self.output)?;
1588        }
1589
1590        // Into inner
1591        writeln!(
1592            &mut self.output,
1593            "    /// Consume and return the inner value"
1594        )?;
1595        writeln!(
1596            &mut self.output,
1597            "    pub fn into_inner(self) -> {} {{",
1598            base_type
1599        )?;
1600        writeln!(&mut self.output, "        self.0")?;
1601        writeln!(&mut self.output, "    }}")?;
1602        writeln!(&mut self.output, "}}")?;
1603        writeln!(&mut self.output)?;
1604
1605        // TryFrom implementation
1606        let try_from_path = self.try_from_path();
1607        writeln!(
1608            &mut self.output,
1609            "impl {}::convert::TryFrom<{}> for {} {{",
1610            try_from_path, base_type, name
1611        )?;
1612        writeln!(&mut self.output, "    type Error = &'static str;")?;
1613        writeln!(&mut self.output)?;
1614        writeln!(
1615            &mut self.output,
1616            "    fn try_from(value: {}) -> Result<Self, Self::Error> {{",
1617            base_type
1618        )?;
1619        writeln!(&mut self.output, "        Self::new(value)")?;
1620        writeln!(&mut self.output, "    }}")?;
1621        writeln!(&mut self.output, "}}")?;
1622        writeln!(&mut self.output)?;
1623
1624        // Encode / Decode / Tagged forwarding impls — required so that this
1625        // type can appear as a field in #[derive(Asn1Sequence)] structs.
1626        writeln!(&mut self.output, "impl Encode for {} {{", name)?;
1627        writeln!(
1628            &mut self.output,
1629            "    fn encode(&self, encoder: &mut Encoder) -> synta::Result<()> {{"
1630        )?;
1631        writeln!(&mut self.output, "        self.0.encode(encoder)")?;
1632        writeln!(&mut self.output, "    }}")?;
1633        writeln!(
1634            &mut self.output,
1635            "    fn encoded_len(&self) -> synta::Result<usize> {{"
1636        )?;
1637        writeln!(&mut self.output, "        self.0.encoded_len()")?;
1638        writeln!(&mut self.output, "    }}")?;
1639        writeln!(&mut self.output, "}}")?;
1640        writeln!(&mut self.output)?;
1641
1642        writeln!(&mut self.output, "impl<'a> Decode<'a> for {} {{", name)?;
1643        writeln!(
1644            &mut self.output,
1645            "    fn decode(decoder: &mut Decoder<'a>) -> synta::Result<Self> {{"
1646        )?;
1647        writeln!(
1648            &mut self.output,
1649            "        {}::decode(decoder).and_then(|v| {{",
1650            base_type
1651        )?;
1652        writeln!(
1653            &mut self.output,
1654            "            Self::new(v).map_err(|_| synta::Error::LengthOverflow)"
1655        )?;
1656        writeln!(&mut self.output, "        }})")?;
1657        writeln!(&mut self.output, "    }}")?;
1658        writeln!(&mut self.output, "}}")?;
1659        writeln!(&mut self.output)?;
1660
1661        writeln!(&mut self.output, "impl Tagged for {} {{", name)?;
1662        writeln!(
1663            &mut self.output,
1664            "    fn tag() -> synta::Tag {{ {}::tag() }}",
1665            base_type
1666        )?;
1667        writeln!(&mut self.output, "}}")?;
1668
1669        self.generate_format_asn1_impl(name, false)?;
1670
1671        Ok(())
1672    }
1673
1674    /// Generate validation code for string constraints
1675    fn generate_string_validation(
1676        &mut self,
1677        var: &str,
1678        base_type: &str,
1679        constraint: &SubtypeConstraint,
1680    ) -> String {
1681        match constraint {
1682            SubtypeConstraint::SizeConstraint(inner) => {
1683                // SIZE constraint - validate length
1684                self.generate_size_validation(var, base_type, inner)
1685            }
1686            SubtypeConstraint::PermittedAlphabet(ranges) => {
1687                // FROM constraint - validate characters
1688                self.generate_alphabet_validation(var, ranges)
1689            }
1690            SubtypeConstraint::Pattern(pattern) => {
1691                // PATTERN constraint - regex validation
1692                self.generate_pattern_validation(var, pattern)
1693            }
1694            SubtypeConstraint::ContainedSubtype(ty) => {
1695                // CONTAINING constraint - optionally validate that the bytes contain a valid encoded value
1696                self.generate_containing_validation(var, ty)
1697            }
1698            SubtypeConstraint::Intersection(constraints) => {
1699                // Combined constraints - validate all of them
1700                self.generate_intersection_string_validation(var, base_type, constraints)
1701            }
1702            _ => {
1703                // Unsupported constraint
1704                format!(
1705                    "        Ok({}({}))",
1706                    self.get_struct_name_from_var(var),
1707                    var
1708                )
1709            }
1710        }
1711    }
1712
1713    /// Generate validation for intersection of string constraints
1714    /// This generates sequential validation: check each constraint in order
1715    fn generate_intersection_string_validation(
1716        &self,
1717        var: &str,
1718        base_type: &str,
1719        constraints: &[SubtypeConstraint],
1720    ) -> String {
1721        let struct_name = self.get_struct_name_from_var(var);
1722
1723        // Separate size and alphabet constraints
1724        let size_constraint = constraints.iter().find_map(|c| {
1725            if let SubtypeConstraint::SizeConstraint(inner) = c {
1726                Some(inner.as_ref())
1727            } else {
1728                None
1729            }
1730        });
1731
1732        let alphabet_constraint = constraints.iter().find_map(|c| {
1733            if let SubtypeConstraint::PermittedAlphabet(ranges) = c {
1734                Some(ranges)
1735            } else {
1736                None
1737            }
1738        });
1739
1740        // Generate combined validation
1741        match (size_constraint, alphabet_constraint) {
1742            (Some(size), Some(alphabet)) => {
1743                // Both SIZE and FROM - validate both
1744                let length_expr = if base_type == "BitString" {
1745                    format!("{}.bit_len()", var)
1746                } else if base_type == "OctetString" {
1747                    format!("{}.as_bytes().len()", var)
1748                } else {
1749                    format!("{}.as_str().len()", var)
1750                };
1751
1752                // Generate size check condition (None = no check needed)
1753                let size_check: Option<String> = match size {
1754                    SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) => {
1755                        Some(format!("{} == {}", length_expr, n))
1756                    }
1757                    SubtypeConstraint::ValueRange { min, max } => {
1758                        let mut parts: Vec<String> = Vec::new();
1759                        if let ConstraintValue::Integer(n) = min {
1760                            if *n == 1 && base_type != "BitString" {
1761                                let obj = length_expr.trim_end_matches(".len()");
1762                                parts.push(format!("!{}.is_empty()", obj));
1763                            } else {
1764                                parts.push(format!("{} >= {}", length_expr, n));
1765                            }
1766                        }
1767                        if let ConstraintValue::Integer(n) = max {
1768                            parts.push(format!("{} <= {}", length_expr, n));
1769                        }
1770                        if parts.is_empty() {
1771                            None
1772                        } else {
1773                            Some(parts.join(" && "))
1774                        }
1775                    }
1776                    _ => None,
1777                };
1778
1779                // Generate alphabet check condition
1780                let range_checks: Vec<String> = alphabet
1781                    .iter()
1782                    .map(|r| {
1783                        if r.min == r.max {
1784                            format!("ch == '{}'", r.min)
1785                        } else {
1786                            format!("(ch >= '{}' && ch <= '{}')", r.min, r.max)
1787                        }
1788                    })
1789                    .collect();
1790                let alphabet_check = range_checks.join(" || ");
1791
1792                // Generate error messages
1793                let size_error = match size {
1794                    SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) => {
1795                        format!("length must equal {}", n)
1796                    }
1797                    SubtypeConstraint::ValueRange { min, max } => {
1798                        let min_str = match min {
1799                            ConstraintValue::Integer(n) => n.to_string(),
1800                            _ => "MIN".to_string(),
1801                        };
1802                        let max_str = match max {
1803                            ConstraintValue::Integer(n) => n.to_string(),
1804                            _ => "MAX".to_string(),
1805                        };
1806                        format!("length must be in range {}..{}", min_str, max_str)
1807                    }
1808                    _ => "invalid length".to_string(),
1809                };
1810
1811                let ranges_display: Vec<String> = alphabet
1812                    .iter()
1813                    .map(|r| {
1814                        if r.min == r.max {
1815                            format!("'{}'", r.min)
1816                        } else {
1817                            format!("'{}'..'{}'", r.min, r.max)
1818                        }
1819                    })
1820                    .collect();
1821                let alphabet_error =
1822                    format!("characters must be from: {}", ranges_display.join(", "));
1823
1824                // Generate validation code
1825                let size_check_block = if let Some(cond) = size_check {
1826                    format!(
1827                        "        // Check SIZE constraint\n        if !({}) {{\n            return Err(\"{}\");\n        }}\n",
1828                        cond, size_error
1829                    )
1830                } else {
1831                    String::new()
1832                };
1833                format!(
1834                    "{}        // Check FROM constraint\n        for ch in {}.as_str().chars() {{\n            if !({}) {{\n                return Err(\"{}\");\n            }}\n        }}\n        Ok({}({}))",
1835                    size_check_block, var, alphabet_check, alphabet_error, struct_name, var
1836                )
1837            }
1838            (Some(size), None) => {
1839                // Only SIZE
1840                self.generate_size_validation(var, base_type, size)
1841            }
1842            (None, Some(alphabet)) => {
1843                // Only FROM
1844                self.generate_alphabet_validation(var, alphabet)
1845            }
1846            (None, None) => {
1847                // No recognized constraints
1848                format!("        Ok({}({}))", struct_name, var)
1849            }
1850        }
1851    }
1852
1853    /// Generate size constraint validation for strings
1854    fn generate_size_validation(
1855        &self,
1856        var: &str,
1857        base_type: &str,
1858        size_constraint: &SubtypeConstraint,
1859    ) -> String {
1860        let length_expr = if base_type == "BitString" {
1861            // BitString::bit_len() returns the number of bits (not bytes)
1862            format!("{}.bit_len()", var)
1863        } else if base_type == "OctetString" {
1864            format!("{}.as_bytes().len()", var)
1865        } else {
1866            format!("{}.as_str().len()", var)
1867        };
1868
1869        let struct_name = self.get_struct_name_from_var(var);
1870
1871        match size_constraint {
1872            SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) => {
1873                format!(
1874                    "        if {} == {} {{\n            Ok({}({}))\n        }} else {{\n            Err(\"length must equal {}\")\n        }}",
1875                    length_expr, n, struct_name, var, n
1876                )
1877            }
1878            SubtypeConstraint::ValueRange { min, max } => {
1879                let mut checks: Vec<String> = Vec::new();
1880                if let ConstraintValue::Integer(n) = min {
1881                    if *n == 1 && base_type != "BitString" {
1882                        let obj = length_expr.trim_end_matches(".len()");
1883                        checks.push(format!("!{}.is_empty()", obj));
1884                    } else {
1885                        checks.push(format!("{} >= {}", length_expr, n));
1886                    }
1887                }
1888                if let ConstraintValue::Integer(n) = max {
1889                    checks.push(format!("{} <= {}", length_expr, n));
1890                }
1891
1892                let min_str = match min {
1893                    ConstraintValue::Integer(n) => n.to_string(),
1894                    _ => "0".to_string(),
1895                };
1896                let max_str = match max {
1897                    ConstraintValue::Integer(n) => n.to_string(),
1898                    _ => "MAX".to_string(),
1899                };
1900
1901                if checks.is_empty() {
1902                    // Unbounded range — all lengths accepted; no check needed.
1903                    format!("        Ok({}({}))", struct_name, var)
1904                } else {
1905                    format!(
1906                        "        if {} {{\n            Ok({}({}))\n        }} else {{\n            Err(\"length must be in range {}..{}\")\n        }}",
1907                        checks.join(" && "), struct_name, var, min_str, max_str
1908                    )
1909                }
1910            }
1911            _ => format!("        Ok({}({}))", struct_name, var),
1912        }
1913    }
1914
1915    /// Generate permitted alphabet validation
1916    fn generate_alphabet_validation(&self, var: &str, ranges: &[CharRange]) -> String {
1917        let struct_name = self.get_struct_name_from_var(var);
1918
1919        // Generate character range checks
1920        let range_checks: Vec<String> = ranges
1921            .iter()
1922            .map(|r| {
1923                if r.min == r.max {
1924                    format!("ch == '{}'", r.min)
1925                } else {
1926                    format!("(ch >= '{}' && ch <= '{}')", r.min, r.max)
1927                }
1928            })
1929            .collect();
1930
1931        let check_expr = range_checks.join(" || ");
1932
1933        let ranges_display: Vec<String> = ranges
1934            .iter()
1935            .map(|r| {
1936                if r.min == r.max {
1937                    format!("'{}'", r.min)
1938                } else {
1939                    format!("'{}'..'{}'", r.min, r.max)
1940                }
1941            })
1942            .collect();
1943
1944        format!(
1945            "        for ch in {}.as_str().chars() {{\n            if !({}) {{\n                return Err(\"characters must be from: {}\");\n            }}\n        }}\n        Ok({}({}))",
1946            var, check_expr, ranges_display.join(", "), struct_name, var
1947        )
1948    }
1949
1950    /// Generate pattern (regex) validation
1951    fn generate_pattern_validation(&mut self, var: &str, pattern: &str) -> String {
1952        let struct_name = self.get_struct_name_from_var(var);
1953
1954        // Generate unique pattern variable name
1955        let pattern_name = format!("PATTERN_{}", self.pattern_counter);
1956        self.pattern_counter += 1;
1957
1958        // Generate regex validation with feature flag
1959        let mut result = String::new();
1960
1961        // Generate static regex pattern with feature flag
1962        result.push_str(&format!(
1963            "        #[cfg(feature = \"regex\")]\n        {{\n            static {}: Lazy<Regex> = Lazy::new(|| Regex::new(r\"{}\").unwrap());\n",
1964            pattern_name, pattern.replace("\"", "\\\"")
1965        ));
1966
1967        // Generate validation check
1968        result.push_str(&format!(
1969            "            if !{}.is_match({}.as_ref()) {{\n",
1970            pattern_name, var
1971        ));
1972        result.push_str(&format!(
1973            "                return Err(format!(\"value does not match pattern: {}\"));\n",
1974            pattern.replace("\"", "\\\"")
1975        ));
1976        result.push_str("            }\n        }\n");
1977
1978        // Generate placeholder for when regex feature is disabled
1979        result.push_str("        #[cfg(not(feature = \"regex\"))]\n");
1980        result.push_str(&format!(
1981            "        // Pattern validation disabled (requires 'regex' feature): {}\n",
1982            pattern
1983        ));
1984
1985        result.push_str(&format!("        Ok({}({}))", struct_name, var));
1986
1987        result
1988    }
1989
1990    fn generate_containing_validation(&self, var: &str, contained_type: &Type) -> String {
1991        let struct_name = self.get_struct_name_from_var(var);
1992        let type_name = self.rust_type(contained_type);
1993
1994        let mut result = String::new();
1995
1996        // Generate validation with feature flag
1997        result.push_str("        #[cfg(feature = \"validate_containing\")]\n");
1998        result.push_str("        {\n");
1999        result.push_str("            use synta::der::Decoder;\n");
2000        result.push_str(&format!(
2001            "            // Validate that {} contains a valid DER-encoded {}\n",
2002            var, type_name
2003        ));
2004        result.push_str(&format!("            let bytes = {}.as_ref();\n", var));
2005        result.push_str(
2006            "            let mut decoder = Decoder::new(bytes, synta::Encoding::Der)?;\n",
2007        );
2008        result.push_str(&format!(
2009            "            let _decoded: {} = decoder.decode().map_err(|e| {{\n",
2010            type_name
2011        ));
2012        result.push_str(&format!(
2013            "                format!(\"invalid {} in CONTAINING constraint: {{}}\", e)\n",
2014            type_name
2015        ));
2016        result.push_str("            })?;\n");
2017        result.push_str("            // Optionally verify complete consumption\n");
2018        result.push_str("            if !decoder.is_empty() {\n");
2019        result.push_str(&format!("                return Err(\"trailing bytes after {} in CONTAINING constraint\".into());\n", type_name));
2020        result.push_str("            }\n");
2021        result.push_str("        }\n");
2022
2023        // Generate placeholder for when validate_containing feature is disabled
2024        result.push_str("        #[cfg(not(feature = \"validate_containing\"))]\n");
2025        result.push_str(&format!(
2026            "        // CONTAINING validation disabled (requires 'validate_containing' feature): {}\n",
2027            type_name
2028        ));
2029
2030        result.push_str(&format!("        Ok({}({}))", struct_name, var));
2031
2032        result
2033    }
2034
2035    /// Helper to extract struct name from variable name
2036    fn get_struct_name_from_var(&self, _var: &str) -> String {
2037        // This is a simplified version - in real code, we'd need context
2038        // For now, just return "Self"
2039        "Self".to_string()
2040    }
2041
2042    /// Format constraint for display in comments
2043    fn format_constraint_display(&self, constraint: &SubtypeConstraint) -> String {
2044        match constraint {
2045            SubtypeConstraint::SingleValue(val) => match val {
2046                ConstraintValue::Integer(n) => n.to_string(),
2047                ConstraintValue::NamedValue(name) => name.clone(),
2048                ConstraintValue::Min => "MIN".to_string(),
2049                ConstraintValue::Max => "MAX".to_string(),
2050            },
2051            SubtypeConstraint::ValueRange { min, max } => {
2052                let min_str = match min {
2053                    ConstraintValue::Integer(n) => n.to_string(),
2054                    ConstraintValue::Min => "MIN".to_string(),
2055                    ConstraintValue::Max => "MAX".to_string(),
2056                    ConstraintValue::NamedValue(n) => n.clone(),
2057                };
2058                let max_str = match max {
2059                    ConstraintValue::Integer(n) => n.to_string(),
2060                    ConstraintValue::Max => "MAX".to_string(),
2061                    ConstraintValue::Min => "MIN".to_string(),
2062                    ConstraintValue::NamedValue(n) => n.clone(),
2063                };
2064                format!("{}..{}", min_str, max_str)
2065            }
2066            SubtypeConstraint::Union(elements) => {
2067                let parts: Vec<String> = elements
2068                    .iter()
2069                    .map(|e| self.format_constraint_display(e))
2070                    .collect();
2071                parts.join(" | ")
2072            }
2073            SubtypeConstraint::Intersection(elements) => {
2074                let parts: Vec<String> = elements
2075                    .iter()
2076                    .map(|e| format!("({})", self.format_constraint_display(e)))
2077                    .collect();
2078                parts.join(" ^ ")
2079            }
2080            SubtypeConstraint::Complement(inner) => {
2081                format!("ALL EXCEPT {}", self.format_constraint_display(inner))
2082            }
2083            SubtypeConstraint::SizeConstraint(inner) => {
2084                format!("SIZE ({})", self.format_constraint_display(inner))
2085            }
2086            SubtypeConstraint::PermittedAlphabet(ranges) => {
2087                let range_strs: Vec<String> = ranges
2088                    .iter()
2089                    .map(|r| {
2090                        if r.min == r.max {
2091                            format!("\"{}\"", r.min)
2092                        } else {
2093                            format!("\"{}\"..\"{}\"", r.min, r.max)
2094                        }
2095                    })
2096                    .collect();
2097                format!("FROM ({})", range_strs.join(" | "))
2098            }
2099            SubtypeConstraint::Pattern(pattern) => {
2100                format!("PATTERN \"{}\"", pattern)
2101            }
2102            SubtypeConstraint::ContainedSubtype(ty) => {
2103                format!("CONTAINING {}", self.rust_type(ty))
2104            }
2105            SubtypeConstraint::InnerType(inner) => {
2106                format!("inner type: {}", self.format_constraint_display(inner))
2107            }
2108            SubtypeConstraint::NamedBitList(bits) => {
2109                let names: Vec<String> = bits
2110                    .iter()
2111                    .map(|b| format!("{}({})", b.name, b.value))
2112                    .collect();
2113                format!("{{ {} }}", names.join(", "))
2114            }
2115        }
2116    }
2117
2118    fn generate_sequence_type(
2119        &mut self,
2120        name: &str,
2121        fields: &[SequenceField],
2122    ) -> Result<(), std::fmt::Error> {
2123        // PRE-PASS: for each field whose underlying type is an anonymous SEQUENCE,
2124        // SET, or CHOICE, generate a named Rust type *before* the struct header so
2125        // that the field can reference it by name.  Replace the anonymous body with
2126        // a TypeRef in the working field list so the rest of codegen stays unchanged.
2127        let mut effective_fields: Vec<SequenceField> = Vec::with_capacity(fields.len());
2128        for field in fields {
2129            if let Some(anon) = anonymous_inner_type(&field.ty) {
2130                let anon_name = format!("{}{}", name, to_pascal_case(&field.name));
2131                self.generate_definition(&Definition {
2132                    name: anon_name.clone(),
2133                    ty: anon.clone(),
2134                })?;
2135                writeln!(&mut self.output)?;
2136                let new_ty = match &field.ty {
2137                    Type::Tagged { tag, .. } => Type::Tagged {
2138                        tag: tag.clone(),
2139                        inner: Box::new(Type::TypeRef(anon_name)),
2140                    },
2141                    _ => Type::TypeRef(anon_name),
2142                };
2143                effective_fields.push(SequenceField {
2144                    name: field.name.clone(),
2145                    ty: new_ty,
2146                    optional: field.optional,
2147                    default: field.default.clone(),
2148                });
2149            } else {
2150                effective_fields.push(field.clone());
2151            }
2152        }
2153        let fields: &[SequenceField] = &effective_fields;
2154
2155        // Derive Default when every field is optional with no explicit ASN.1 DEFAULT —
2156        // in that case all fields map to None, which is always the correct default.
2157        let all_optional = fields.iter().all(|f| f.optional && f.default.is_none());
2158        if all_optional {
2159            writeln!(
2160                &mut self.output,
2161                "#[derive(Debug, Clone, PartialEq, Default)]"
2162            )?;
2163        } else {
2164            writeln!(&mut self.output, "#[derive(Debug, Clone, PartialEq)]")?;
2165        }
2166        let attr = self.derive_cfg_attr("derive(Asn1Sequence)");
2167        writeln!(&mut self.output, "{}", attr)?;
2168
2169        // Add lifetime parameter if any field needs it
2170        let needs_lifetime = self.sequence_needs_lifetime(fields);
2171        if needs_lifetime {
2172            writeln!(&mut self.output, "pub struct {}<'a> {{", name)?;
2173            // Track that this type has a lifetime
2174            self.types_with_lifetimes.insert(name.to_string());
2175        } else {
2176            writeln!(&mut self.output, "pub struct {} {{", name)?;
2177        }
2178
2179        for field in fields {
2180            self.generate_field(field)?;
2181        }
2182
2183        writeln!(&mut self.output, "}}")?;
2184
2185        self.generate_format_asn1_impl(name, needs_lifetime)?;
2186
2187        Ok(())
2188    }
2189
2190    fn generate_set_type(
2191        &mut self,
2192        name: &str,
2193        fields: &[SequenceField],
2194    ) -> Result<(), std::fmt::Error> {
2195        // PRE-PASS: same anonymous-inner-type handling as generate_sequence_type.
2196        let mut effective_fields: Vec<SequenceField> = Vec::with_capacity(fields.len());
2197        for field in fields {
2198            if let Some(anon) = anonymous_inner_type(&field.ty) {
2199                let anon_name = format!("{}{}", name, to_pascal_case(&field.name));
2200                self.generate_definition(&Definition {
2201                    name: anon_name.clone(),
2202                    ty: anon.clone(),
2203                })?;
2204                writeln!(&mut self.output)?;
2205                let new_ty = match &field.ty {
2206                    Type::Tagged { tag, .. } => Type::Tagged {
2207                        tag: tag.clone(),
2208                        inner: Box::new(Type::TypeRef(anon_name)),
2209                    },
2210                    _ => Type::TypeRef(anon_name),
2211                };
2212                effective_fields.push(SequenceField {
2213                    name: field.name.clone(),
2214                    ty: new_ty,
2215                    optional: field.optional,
2216                    default: field.default.clone(),
2217                });
2218            } else {
2219                effective_fields.push(field.clone());
2220            }
2221        }
2222        let fields: &[SequenceField] = &effective_fields;
2223
2224        writeln!(&mut self.output, "#[derive(Debug, Clone, PartialEq)]")?;
2225        let attr = self.derive_cfg_attr("derive(Asn1Set)");
2226        writeln!(&mut self.output, "{}", attr)?;
2227
2228        // Add lifetime parameter if any field needs it
2229        let needs_lifetime = self.sequence_needs_lifetime(fields);
2230        if needs_lifetime {
2231            writeln!(&mut self.output, "pub struct {}<'a> {{", name)?;
2232            // Track that this type has a lifetime
2233            self.types_with_lifetimes.insert(name.to_string());
2234        } else {
2235            writeln!(&mut self.output, "pub struct {} {{", name)?;
2236        }
2237
2238        for field in fields {
2239            self.generate_field(field)?;
2240        }
2241
2242        writeln!(&mut self.output, "}}")?;
2243
2244        self.generate_format_asn1_impl(name, needs_lifetime)?;
2245
2246        Ok(())
2247    }
2248
2249    fn generate_choice_type(
2250        &mut self,
2251        name: &str,
2252        variants: &[ChoiceVariant],
2253    ) -> Result<(), std::fmt::Error> {
2254        // PRE-PASS: for each variant whose underlying type is an anonymous SEQUENCE,
2255        // SET, or CHOICE, generate a named Rust type *before* the enum header so
2256        // that the variant can reference it by name.  Replace the anonymous body with
2257        // a TypeRef in the working variant list so the rest of codegen stays unchanged.
2258        let mut effective_variants: Vec<ChoiceVariant> = Vec::with_capacity(variants.len());
2259        for variant in variants {
2260            if let Some(anon) = anonymous_inner_type(&variant.ty) {
2261                let anon_name = format!("{}{}", name, to_pascal_case(&variant.name));
2262                self.generate_definition(&Definition {
2263                    name: anon_name.clone(),
2264                    ty: anon.clone(),
2265                })?;
2266                writeln!(&mut self.output)?;
2267                let new_ty = match &variant.ty {
2268                    Type::Tagged { tag, .. } => Type::Tagged {
2269                        tag: tag.clone(),
2270                        inner: Box::new(Type::TypeRef(anon_name)),
2271                    },
2272                    _ => Type::TypeRef(anon_name),
2273                };
2274                effective_variants.push(ChoiceVariant {
2275                    name: variant.name.clone(),
2276                    ty: new_ty,
2277                });
2278            } else {
2279                effective_variants.push(variant.clone());
2280            }
2281        }
2282        let variants: &[ChoiceVariant] = &effective_variants;
2283
2284        writeln!(&mut self.output, "#[derive(Debug, Clone, PartialEq)]")?;
2285        let attr = self.derive_cfg_attr("derive(Asn1Choice)");
2286        writeln!(&mut self.output, "{}", attr)?;
2287
2288        // Add lifetime parameter if any variant needs it
2289        let needs_lifetime = self.choice_needs_lifetime(variants);
2290        if needs_lifetime {
2291            writeln!(&mut self.output, "pub enum {}<'a> {{", name)?;
2292            // Track that this type has a lifetime
2293            self.types_with_lifetimes.insert(name.to_string());
2294        } else {
2295            writeln!(&mut self.output, "pub enum {} {{", name)?;
2296        }
2297
2298        for variant in variants {
2299            let variant_name = to_pascal_case(&variant.name);
2300
2301            // For IMPLICIT context-specific tagged variants, force owned string types.
2302            // IMPLICIT tags require TLV reconstruction during decode (the context tag
2303            // replaces the inner type's own tag), so borrowed string types cannot borrow
2304            // from the original input — the reconstruction buffer is a local allocation.
2305            let rust_type = if let Type::Tagged { tag, .. } = &variant.ty {
2306                if tag.class == TagClass::ContextSpecific && tag.tagging == Tagging::Implicit {
2307                    let saved = self.config.string_type_mode.clone();
2308                    self.config.string_type_mode = StringTypeMode::Owned;
2309                    let ty = self.inline_sequence_of_types(&variant.ty);
2310                    self.config.string_type_mode = saved;
2311                    ty
2312                } else {
2313                    self.inline_sequence_of_types(&variant.ty)
2314                }
2315            } else {
2316                self.inline_sequence_of_types(&variant.ty)
2317            };
2318
2319            // If it's a tagged type, emit the attribute with the correct tagging mode
2320            if let Type::Tagged { tag, .. } = &variant.ty {
2321                match tag.class {
2322                    TagClass::ContextSpecific => {
2323                        let tagging_str = match tag.tagging {
2324                            Tagging::Explicit => "explicit",
2325                            Tagging::Implicit => "implicit",
2326                        };
2327                        let attr = self.field_derive_cfg_attr(&format!(
2328                            "asn1(tag({}, {}))",
2329                            tag.number, tagging_str
2330                        ));
2331                        writeln!(&mut self.output, "{}", attr)?;
2332                    }
2333                    TagClass::Application => {
2334                        writeln!(
2335                            &mut self.output,
2336                            "    // APPLICATION [{}] -- use asn1(application_tag) when supported",
2337                            tag.number
2338                        )?;
2339                        let attr = self
2340                            .field_derive_cfg_attr(&format!("asn1(tag({}, explicit))", tag.number));
2341                        writeln!(&mut self.output, "{}", attr)?;
2342                    }
2343                    TagClass::Universal => {
2344                        writeln!(&mut self.output, "    // UNIVERSAL [{}]", tag.number)?;
2345                    }
2346                    TagClass::Private => {
2347                        writeln!(&mut self.output, "    // PRIVATE [{}]", tag.number)?;
2348                    }
2349                }
2350            }
2351
2352            writeln!(&mut self.output, "    {}({}),", variant_name, rust_type)?;
2353        }
2354
2355        writeln!(&mut self.output, "}}")?;
2356
2357        self.generate_format_asn1_impl(name, needs_lifetime)?;
2358
2359        Ok(())
2360    }
2361
2362    fn generate_sequence_of_type(
2363        &mut self,
2364        name: &str,
2365        inner: &Type,
2366        size_constraint: Option<&SizeConstraint>,
2367    ) -> Result<(), std::fmt::Error> {
2368        // Handle anonymous inner types: SEQUENCE OF SEQUENCE { ... } / SET { ... } / CHOICE { ... }
2369        // Generate a named element type so the collection has a concrete Rust type to reference.
2370        if matches!(inner, Type::Sequence(_) | Type::Set(_) | Type::Choice(_)) {
2371            let element_name = format!("{}Element", name);
2372            let element_def = Definition {
2373                name: element_name.clone(),
2374                ty: inner.clone(),
2375            };
2376            self.generate_definition(&element_def)?;
2377            writeln!(&mut self.output)?;
2378            if let Some(constraint) = size_constraint {
2379                let constraint_str = self.format_size_constraint(constraint);
2380                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2381            }
2382            let rust_type = format!("Vec<{}>", element_name);
2383            // Construct the SequenceOf type for lifetime tracking
2384            let seq_of_type = Type::SequenceOf(Box::new(inner.clone()), size_constraint.cloned());
2385            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2386            return Ok(());
2387        }
2388
2389        // Handle inner type constraints (Phase 3)
2390        if let Type::Constrained {
2391            base_type,
2392            constraint,
2393        } = inner
2394        {
2395            // Generate element newtype with constrained validation
2396            let element_name = format!("{}Element", name);
2397
2398            // Generate the constrained element type
2399            match base_type.as_ref() {
2400                Type::Integer(_, _) => {
2401                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2402                        self.generate_constrained_integer(&element_name, subtype)?;
2403                    }
2404                }
2405                Type::IA5String(_)
2406                | Type::Utf8String(_)
2407                | Type::PrintableString(_)
2408                | Type::TeletexString(_)
2409                | Type::UniversalString(_)
2410                | Type::BmpString(_)
2411                | Type::GeneralString(_)
2412                | Type::NumericString(_)
2413                | Type::VisibleString(_) => {
2414                    let base_type_str = self.rust_type(base_type);
2415                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2416                        self.generate_constrained_string(&element_name, &base_type_str, subtype)?;
2417                    }
2418                }
2419                _ => {
2420                    // For other types, fall back to simple type alias
2421                    let inner_type = self.rust_type(inner);
2422                    writeln!(
2423                        &mut self.output,
2424                        "pub type {} = {};",
2425                        element_name, inner_type
2426                    )?;
2427                }
2428            }
2429
2430            writeln!(&mut self.output)?;
2431
2432            // Generate the collection type using the element type
2433            if let Some(constraint) = size_constraint {
2434                let constraint_str = self.format_size_constraint(constraint);
2435                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2436            }
2437            writeln!(
2438                &mut self.output,
2439                "pub type {} = Vec<{}>;",
2440                name, element_name
2441            )?;
2442        } else {
2443            // No constraint on inner type - generate simple type alias
2444            if let Some(constraint) = size_constraint {
2445                let constraint_str = self.format_size_constraint(constraint);
2446                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2447            }
2448            let inner_type = self.rust_type(inner);
2449            let rust_type = format!("Vec<{}>", inner_type);
2450            // Construct the SequenceOf/SetOf type for lifetime tracking
2451            let seq_of_type = Type::SequenceOf(Box::new(inner.clone()), size_constraint.cloned());
2452            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2453        }
2454        Ok(())
2455    }
2456
2457    fn generate_set_of_type(
2458        &mut self,
2459        name: &str,
2460        inner: &Type,
2461        size_constraint: Option<&SizeConstraint>,
2462    ) -> Result<(), std::fmt::Error> {
2463        // Handle anonymous inner types: SET OF SEQUENCE { ... } / SET { ... } / CHOICE { ... }
2464        if matches!(inner, Type::Sequence(_) | Type::Set(_) | Type::Choice(_)) {
2465            let element_name = format!("{}Element", name);
2466            let element_def = Definition {
2467                name: element_name.clone(),
2468                ty: inner.clone(),
2469            };
2470            self.generate_definition(&element_def)?;
2471            writeln!(&mut self.output)?;
2472            if let Some(constraint) = size_constraint {
2473                let constraint_str = self.format_size_constraint(constraint);
2474                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2475            }
2476            let rust_type = format!("SetOf<{}>", element_name);
2477            // Construct the SetOf type for lifetime tracking
2478            let seq_of_type = Type::SetOf(Box::new(inner.clone()), size_constraint.cloned());
2479            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2480            return Ok(());
2481        }
2482
2483        // Handle inner type constraints (Phase 3) - same as SEQUENCE OF
2484        if let Type::Constrained {
2485            base_type,
2486            constraint,
2487        } = inner
2488        {
2489            // Generate element newtype with constrained validation
2490            let element_name = format!("{}Element", name);
2491
2492            // Generate the constrained element type
2493            match base_type.as_ref() {
2494                Type::Integer(_, _) => {
2495                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2496                        self.generate_constrained_integer(&element_name, subtype)?;
2497                    }
2498                }
2499                Type::IA5String(_)
2500                | Type::Utf8String(_)
2501                | Type::PrintableString(_)
2502                | Type::TeletexString(_)
2503                | Type::UniversalString(_)
2504                | Type::BmpString(_)
2505                | Type::GeneralString(_)
2506                | Type::NumericString(_)
2507                | Type::VisibleString(_) => {
2508                    let base_type_str = self.rust_type(base_type);
2509                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2510                        self.generate_constrained_string(&element_name, &base_type_str, subtype)?;
2511                    }
2512                }
2513                _ => {
2514                    // For other types, fall back to simple type alias
2515                    let inner_type = self.rust_type(inner);
2516                    writeln!(
2517                        &mut self.output,
2518                        "pub type {} = {};",
2519                        element_name, inner_type
2520                    )?;
2521                }
2522            }
2523
2524            writeln!(&mut self.output)?;
2525
2526            // Generate the collection type using the element type
2527            if let Some(constraint) = size_constraint {
2528                let constraint_str = self.format_size_constraint(constraint);
2529                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2530            }
2531            writeln!(
2532                &mut self.output,
2533                "pub type {} = SetOf<{}>;",
2534                name, element_name
2535            )?;
2536        } else {
2537            // No constraint on inner type - generate simple type alias
2538            if let Some(constraint) = size_constraint {
2539                let constraint_str = self.format_size_constraint(constraint);
2540                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2541            }
2542            let inner_type = self.rust_type(inner);
2543            let rust_type = format!("SetOf<{}>", inner_type);
2544            // Construct the SetOf type for lifetime tracking
2545            let seq_of_type = Type::SetOf(Box::new(inner.clone()), size_constraint.cloned());
2546            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2547        }
2548        Ok(())
2549    }
2550
2551    /// Return `true` when `ty` resolves to `Type::Any` or `Type::AnyDefinedBy`,
2552    /// following up to one level of TypeRef indirection through `type_definitions`.
2553    fn resolves_to_any(&self, ty: &Type) -> bool {
2554        match ty {
2555            Type::Any | Type::AnyDefinedBy(_) => true,
2556            Type::TypeRef(name) => {
2557                let clean = name.trim_end_matches("{}");
2558                matches!(
2559                    self.type_definitions.get(clean),
2560                    Some(Type::Any) | Some(Type::AnyDefinedBy(_))
2561                )
2562            }
2563            _ => false,
2564        }
2565    }
2566
2567    /// Return `true` when `ty` resolves to `Type::Choice`,
2568    /// following up to one level of TypeRef indirection through `type_definitions`
2569    /// or via the `config.known_choice_types` set (for imported types).
2570    ///
2571    /// Per ASN.1 X.680 §31.2.7, IMPLICIT tagging cannot be applied to CHOICE
2572    /// types; such tags must be treated as EXPLICIT.
2573    fn resolves_to_choice(&self, ty: &Type) -> bool {
2574        match ty {
2575            Type::Choice(_) => true,
2576            Type::TypeRef(name) => {
2577                let clean = name.trim_end_matches("{}");
2578                self.config.known_choice_types.contains(clean)
2579                    || matches!(self.type_definitions.get(clean), Some(Type::Choice(_)))
2580            }
2581            _ => false,
2582        }
2583    }
2584
2585    fn generate_field(&mut self, field: &SequenceField) -> Result<(), std::fmt::Error> {
2586        let field_name = to_snake_case(&field.name);
2587        // Inline SEQUENCE OF and SET OF types to enable derive macro Vec<T<'a>> detection
2588        let rust_type = self.inline_sequence_of_types(&field.ty);
2589
2590        // Add attribute for tagged fields
2591        if let Type::Tagged { tag, inner } = &field.ty {
2592            let tagging = match tag.tagging {
2593                Tagging::Explicit => "explicit",
2594                // ASN.1 X.680 §31.2.7: IMPLICIT tagging cannot be applied to CHOICE;
2595                // such a tag is automatically treated as EXPLICIT.
2596                // Note: IMPLICIT ANY is NOT promoted — the rawder mechanism handles
2597                // it specially via the any_as_raw_der config option.
2598                Tagging::Implicit if self.resolves_to_choice(inner) => "explicit",
2599                Tagging::Implicit => "implicit",
2600            };
2601            match tag.class {
2602                TagClass::ContextSpecific => {
2603                    let attr = self
2604                        .field_derive_cfg_attr(&format!("asn1(tag({}, {}))", tag.number, tagging));
2605                    writeln!(&mut self.output, "{}", attr)?;
2606                    // When any_as_raw_der = true, IMPLICIT-tagged ANY fields are emitted as
2607                    // RawDer<'a>.  The type alias (e.g. `CertificateSet<'a> = RawDer<'a>`) is
2608                    // opaque to the derive macro, which cannot detect that the aliased type is
2609                    // RawDer and therefore cannot select the correct encode path.  Emit a
2610                    // `rawder` marker attribute so the derive's encode path can use the direct
2611                    // content-write approach instead of the "encode then strip" approach.
2612                    if self.config.any_as_raw_der
2613                        && tag.tagging == Tagging::Implicit
2614                        && self.resolves_to_any(inner)
2615                    {
2616                        let rawder_attr = self.field_derive_cfg_attr("asn1(rawder)");
2617                        writeln!(&mut self.output, "{}", rawder_attr)?;
2618                    }
2619                }
2620                TagClass::Application => {
2621                    writeln!(
2622                        &mut self.output,
2623                        "    // APPLICATION [{} {}] -- use asn1(application_tag) when supported",
2624                        tag.number, tagging
2625                    )?;
2626                    let attr = self
2627                        .field_derive_cfg_attr(&format!("asn1(tag({}, {}))", tag.number, tagging));
2628                    writeln!(&mut self.output, "{}", attr)?;
2629                }
2630                TagClass::Universal => {
2631                    writeln!(
2632                        &mut self.output,
2633                        "    // UNIVERSAL [{}] {}",
2634                        tag.number, tagging
2635                    )?;
2636                }
2637                TagClass::Private => {
2638                    writeln!(
2639                        &mut self.output,
2640                        "    // PRIVATE [{}] {}",
2641                        tag.number, tagging
2642                    )?;
2643                }
2644            }
2645        }
2646
2647        // Add optional attribute
2648        // Fields with DEFAULT values are implicitly optional in ASN.1
2649        if field.optional || field.default.is_some() {
2650            let attr = self.field_derive_cfg_attr("asn1(optional)");
2651            writeln!(&mut self.output, "{}", attr)?;
2652        }
2653
2654        // Override with RawDer<'a> for fields listed in raw_der_fields.
2655        let rust_type = if self.config.raw_der_fields.contains(&field_name) {
2656            "RawDer<'a>".to_string()
2657        } else {
2658            rust_type
2659        };
2660
2661        // Fields are Option<T> if explicitly OPTIONAL or have DEFAULT values
2662        let final_type = if field.optional || field.default.is_some() {
2663            format!("Option<{}>", rust_type)
2664        } else {
2665            rust_type
2666        };
2667
2668        writeln!(&mut self.output, "    pub {}: {},", field_name, final_type)?;
2669
2670        Ok(())
2671    }
2672
2673    /// Generate a subtype definition (TypeRef with additional constraints)
2674    fn generate_subtype(
2675        &mut self,
2676        type_name: &str,
2677        base_type: &Type,
2678        constraint: &SubtypeConstraint,
2679    ) -> Result<(), std::fmt::Error> {
2680        let base_type_name = self.rust_type(base_type);
2681        let constraint_display = self.format_constraint_display(constraint);
2682
2683        // Generate doc comment
2684        writeln!(
2685            &mut self.output,
2686            "/// {} ({})",
2687            base_type_name, constraint_display
2688        )?;
2689
2690        // Generate newtype struct
2691        writeln!(
2692            &mut self.output,
2693            "#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]"
2694        )?;
2695        writeln!(
2696            &mut self.output,
2697            "pub struct {}({});",
2698            type_name, base_type_name
2699        )?;
2700        writeln!(&mut self.output)?;
2701
2702        writeln!(&mut self.output, "impl {} {{", type_name)?;
2703
2704        // Generate validation code
2705        // The approach: convert the base type to its underlying primitive, then validate
2706        let validation_code = self.generate_subtype_validation("val", constraint)?;
2707
2708        writeln!(
2709            &mut self.output,
2710            "    /// Create a new {} with validation",
2711            type_name
2712        )?;
2713        writeln!(
2714            &mut self.output,
2715            "    pub fn new(value: {}) -> Result<Self, &'static str> {{",
2716            base_type_name
2717        )?;
2718
2719        // Determine if this is an integer or string constraint
2720        let is_integer_constraint = matches!(
2721            constraint,
2722            SubtypeConstraint::SingleValue(_)
2723                | SubtypeConstraint::ValueRange { .. }
2724                | SubtypeConstraint::Union(_)
2725                | SubtypeConstraint::Intersection(_)
2726                | SubtypeConstraint::Complement(_)
2727        );
2728
2729        let description = self.generate_constraint_description(constraint);
2730
2731        if is_integer_constraint {
2732            writeln!(&mut self.output, "        let val = value.into_inner();")?;
2733            writeln!(&mut self.output, "        if {} {{", validation_code)?;
2734            writeln!(&mut self.output, "            Ok({}(value))", type_name)?;
2735            writeln!(&mut self.output, "        }} else {{")?;
2736            writeln!(&mut self.output, "            Err(\"{}\")", description)?;
2737            writeln!(&mut self.output, "        }}")?;
2738        } else {
2739            // String subtype - work with the string value directly
2740            let validation_code =
2741                self.generate_string_validation("value", &base_type_name, constraint);
2742            write!(&mut self.output, "{}", validation_code)?;
2743        }
2744
2745        writeln!(&mut self.output, "    }}")?;
2746        writeln!(&mut self.output)?;
2747
2748        // Generate unchecked constructor
2749        writeln!(
2750            &mut self.output,
2751            "    /// Create without validation (use with caution)"
2752        )?;
2753        writeln!(
2754            &mut self.output,
2755            "    pub const fn new_unchecked(value: {}) -> Self {{",
2756            base_type_name
2757        )?;
2758        writeln!(&mut self.output, "        {}(value)", type_name)?;
2759        writeln!(&mut self.output, "    }}")?;
2760        writeln!(&mut self.output)?;
2761
2762        // Generate get method
2763        writeln!(&mut self.output, "    /// Get the inner value")?;
2764        writeln!(
2765            &mut self.output,
2766            "    pub const fn get(&self) -> &{} {{",
2767            base_type_name
2768        )?;
2769        writeln!(&mut self.output, "        &self.0")?;
2770        writeln!(&mut self.output, "    }}")?;
2771        writeln!(&mut self.output)?;
2772
2773        // Generate into_inner method
2774        writeln!(
2775            &mut self.output,
2776            "    /// Consume and return the inner value"
2777        )?;
2778        writeln!(
2779            &mut self.output,
2780            "    pub fn into_inner(self) -> {} {{",
2781            base_type_name
2782        )?;
2783        writeln!(&mut self.output, "        self.0")?;
2784        writeln!(&mut self.output, "    }}")?;
2785
2786        writeln!(&mut self.output, "}}")?;
2787        writeln!(&mut self.output)?;
2788
2789        // Generate TryFrom impl
2790        let try_from_path = self.try_from_path();
2791        writeln!(
2792            &mut self.output,
2793            "impl {}::convert::TryFrom<{}> for {} {{",
2794            try_from_path, base_type_name, type_name
2795        )?;
2796        writeln!(&mut self.output, "    type Error = &'static str;")?;
2797        writeln!(&mut self.output)?;
2798        writeln!(
2799            &mut self.output,
2800            "    fn try_from(value: {}) -> Result<Self, Self::Error> {{",
2801            base_type_name
2802        )?;
2803        writeln!(&mut self.output, "        Self::new(value)")?;
2804        writeln!(&mut self.output, "    }}")?;
2805        writeln!(&mut self.output, "}}")?;
2806
2807        Ok(())
2808    }
2809
2810    /// Generate validation code for subtype constraints
2811    fn generate_subtype_validation(
2812        &mut self,
2813        var: &str,
2814        constraint: &SubtypeConstraint,
2815    ) -> Result<String, std::fmt::Error> {
2816        // Delegate to generate_constraint_validation which works with i64
2817        // This assumes the base type can be converted to Integer
2818        Ok(self.generate_constraint_validation(var, constraint))
2819    }
2820
2821    /// Emit `impl [<'a>] TypeName[<'a>] { pub fn to_der(...) pub fn format_asn1(...) }`
2822    /// after any generated struct/enum that implements `Encode`.
2823    ///
2824    /// `to_der` encodes `self` to DER bytes and returns a `synta::Result<Vec<u8>>`.
2825    /// `format_asn1` encodes `self` to DER and delegates to
2826    /// `synta::format_asn1_bytes`, which supports both raw hex and
2827    /// human-readable ASN.1 text output.
2828    fn generate_format_asn1_impl(
2829        &mut self,
2830        name: &str,
2831        has_lifetime: bool,
2832    ) -> Result<(), std::fmt::Error> {
2833        writeln!(&mut self.output)?;
2834        if has_lifetime {
2835            writeln!(&mut self.output, "impl<'a> {}<'a> {{", name)?;
2836        } else {
2837            writeln!(&mut self.output, "impl {} {{", name)?;
2838        }
2839        // from_der()
2840        if has_lifetime {
2841            writeln!(
2842                &mut self.output,
2843                "    /// Parse a DER-encoded `{}` from borrowed bytes.",
2844                name
2845            )?;
2846            writeln!(&mut self.output, "    ///")?;
2847            writeln!(
2848                &mut self.output,
2849                "    /// The returned value borrows from `data` for zero-copy fields."
2850            )?;
2851            writeln!(
2852                &mut self.output,
2853                "    pub fn from_der(data: &'a [u8]) -> synta::Result<Self> {{"
2854            )?;
2855        } else {
2856            writeln!(
2857                &mut self.output,
2858                "    /// Parse a DER-encoded `{}` from bytes.",
2859                name
2860            )?;
2861            writeln!(
2862                &mut self.output,
2863                "    pub fn from_der(data: &[u8]) -> synta::Result<Self> {{"
2864            )?;
2865        }
2866        writeln!(
2867            &mut self.output,
2868            "        synta::Decoder::new(data, synta::Encoding::Der).decode::<Self>()"
2869        )?;
2870        writeln!(&mut self.output, "    }}")?;
2871        writeln!(&mut self.output)?;
2872        // to_der()
2873        writeln!(&mut self.output, "    /// Encode this value to DER bytes.")?;
2874        writeln!(&mut self.output, "    ///")?;
2875        writeln!(
2876            &mut self.output,
2877            "    /// Returns a [`synta::Result`] wrapping the DER-encoded bytes,"
2878        )?;
2879        writeln!(&mut self.output, "    /// or an error if encoding fails.")?;
2880        writeln!(
2881            &mut self.output,
2882            "    pub fn to_der(&self) -> synta::Result<Vec<u8>> {{"
2883        )?;
2884        writeln!(&mut self.output, "        use synta::Encode;")?;
2885        writeln!(
2886            &mut self.output,
2887            "        let mut encoder = synta::Encoder::new(synta::Encoding::Der);"
2888        )?;
2889        writeln!(&mut self.output, "        self.encode(&mut encoder)?;")?;
2890        writeln!(&mut self.output, "        encoder.finish()")?;
2891        writeln!(&mut self.output, "    }}")?;
2892        // format_asn1()
2893        writeln!(&mut self.output)?;
2894        writeln!(
2895            &mut self.output,
2896            "    /// Format the encoded DER bytes of this value."
2897        )?;
2898        writeln!(&mut self.output, "    ///")?;
2899        writeln!(
2900            &mut self.output,
2901            "    /// `mode` controls the output style:"
2902        )?;
2903        writeln!(
2904            &mut self.output,
2905            "    /// - [`synta::Asn1FormatMode::Hex`] — space-separated uppercase hex bytes"
2906        )?;
2907        writeln!(
2908            &mut self.output,
2909            "    /// - [`synta::Asn1FormatMode::Text`] — indented human-readable ASN.1 dump"
2910        )?;
2911        writeln!(
2912            &mut self.output,
2913            "    pub fn format_asn1(&self, mode: synta::Asn1FormatMode) -> String {{"
2914        )?;
2915        writeln!(&mut self.output, "        use synta::Encode;")?;
2916        writeln!(
2917            &mut self.output,
2918            "        let mut encoder = synta::Encoder::new(synta::Encoding::Der);"
2919        )?;
2920        writeln!(
2921            &mut self.output,
2922            "        if self.encode(&mut encoder).is_err() {{"
2923        )?;
2924        writeln!(
2925            &mut self.output,
2926            "            return String::from(\"<encode error>\");"
2927        )?;
2928        writeln!(&mut self.output, "        }}")?;
2929        writeln!(&mut self.output, "        match encoder.finish() {{")?;
2930        writeln!(
2931            &mut self.output,
2932            "            Ok(bytes) => synta::format_asn1_bytes(&bytes, mode),"
2933        )?;
2934        writeln!(
2935            &mut self.output,
2936            "            Err(_) => String::from(\"<encode error>\"),"
2937        )?;
2938        writeln!(&mut self.output, "        }}")?;
2939        writeln!(&mut self.output, "    }}")?;
2940        writeln!(&mut self.output, "}}")?;
2941        Ok(())
2942    }
2943
2944    /// Return the Rust type name for an ASN.1 string type, respecting the
2945    /// configured [`StringTypeMode`].
2946    ///
2947    /// Types that have a zero-copy `Ref` variant (e.g. `OctetString` /
2948    /// `OctetStringRef<'a>`) use `borrowed` in [`StringTypeMode::Borrowed`]
2949    /// mode. Types that only have an owned form (e.g. `TeletexString`) are
2950    /// returned as-is regardless of mode.
2951    #[inline]
2952    fn string_rust_type(&self, owned: &str, borrowed: &str) -> String {
2953        match self.config.string_type_mode {
2954            StringTypeMode::Owned => owned.to_string(),
2955            StringTypeMode::Borrowed => format!("{}<'a>", borrowed),
2956        }
2957    }
2958
2959    fn rust_type(&self, ty: &Type) -> String {
2960        match ty {
2961            Type::Integer(_, _) => "Integer".to_string(),
2962            Type::Enumerated(_) => "Enumerated".to_string(),
2963            Type::Real => "f64".to_string(),
2964            Type::Boolean => "Boolean".to_string(),
2965            // Types with both owned and zero-copy borrowed variants
2966            Type::OctetString(_) => self.string_rust_type("OctetString", "OctetStringRef"),
2967            Type::BitString(_) => self.string_rust_type("BitString", "BitStringRef"),
2968            Type::Utf8String(_) => self.string_rust_type("Utf8String", "Utf8StringRef"),
2969            Type::PrintableString(_) => {
2970                self.string_rust_type("PrintableString", "PrintableStringRef")
2971            }
2972            Type::IA5String(_) => self.string_rust_type("IA5String", "IA5StringRef"),
2973            Type::ObjectIdentifier => "ObjectIdentifier".to_string(),
2974            Type::Null => "Null".to_string(),
2975            // Types without a Ref variant — always owned
2976            Type::TeletexString(_) => "TeletexString".to_string(),
2977            Type::UniversalString(_) => "UniversalString".to_string(),
2978            Type::BmpString(_) => "BmpString".to_string(),
2979            Type::GeneralString(_) => "GeneralString".to_string(),
2980            Type::NumericString(_) => "NumericString".to_string(),
2981            Type::VisibleString(_) => "VisibleString".to_string(),
2982            Type::UtcTime => "UtcTime".to_string(),
2983            Type::GeneralizedTime => "GeneralizedTime".to_string(),
2984            Type::TypeRef(name) => {
2985                let type_name = to_pascal_case(name);
2986
2987                // Check if this type requires a specific lifetime parameter.
2988                // Applies to both explicitly imported types (IMPORTS section) and
2989                // bare TypeRefs that reference types from other schemas without a
2990                // formal IMPORTS declaration — `imported_types` membership is not
2991                // required for the lookup.
2992                if let Some(lifetime) = self.config.imported_type_lifetimes.get(name) {
2993                    return format!("{}<{}>", type_name, lifetime);
2994                }
2995
2996                // Check if this is a locally-generated type that has a lifetime
2997                if self.types_with_lifetimes.contains(&type_name) {
2998                    return format!("{}<'a>", type_name);
2999                }
3000
3001                type_name
3002            }
3003            Type::Class(_) => "/* class */".to_string(),
3004            Type::Sequence(_) => "/* nested sequence */".to_string(),
3005            Type::Set(_) => "/* nested set */".to_string(),
3006            Type::Choice(_) => "/* nested choice */".to_string(),
3007            Type::SequenceOf(inner, _) => format!("Vec<{}>", self.rust_type(inner)),
3008            Type::SetOf(inner, _) => format!("SetOf<{}>", self.rust_type(inner)),
3009            Type::Tagged { inner, .. } => self.rust_type(inner),
3010            Type::Constrained { base_type, .. } => self.rust_type(base_type),
3011            Type::Any => {
3012                if self.config.any_as_raw_der {
3013                    "RawDer<'a>".to_string()
3014                } else {
3015                    "Element<'a>".to_string()
3016                }
3017            }
3018            Type::AnyDefinedBy(_) => {
3019                if self.config.any_as_raw_der {
3020                    "RawDer<'a>".to_string()
3021                } else {
3022                    "Element<'a>".to_string()
3023                }
3024            }
3025        }
3026    }
3027
3028    /// Return `true` when `ty` requires a `'a` lifetime parameter.
3029    ///
3030    /// This is `true` when any of the following apply:
3031    /// - The type is `OctetString`, `BitString`, `Utf8String`, `PrintableString`,
3032    ///   or `IA5String` and [`StringTypeMode::Borrowed`] is active.
3033    /// - The type is `ANY` / `ANY DEFINED BY` (always `Element<'a>`).
3034    /// - The type is a reference to an imported type whose lifetime was declared
3035    ///   via [`CodeGenConfig::imported_type_lifetimes`].
3036    /// - The type is a reference to a locally-generated type that was previously
3037    ///   determined to need a lifetime (fixed-point prescan).
3038    /// - The type is a `SEQUENCE OF`, `SET OF`, tagged, or constrained wrapper
3039    ///   around a type that needs a lifetime.
3040    fn type_needs_lifetime(&self, ty: &Type) -> bool {
3041        match ty {
3042            // Types with Ref variants: need lifetime in Borrowed mode
3043            Type::OctetString(_)
3044            | Type::BitString(_)
3045            | Type::Utf8String(_)
3046            | Type::PrintableString(_)
3047            | Type::IA5String(_) => self.config.string_type_mode == StringTypeMode::Borrowed,
3048            Type::Any => true,             // Element<'a>
3049            Type::AnyDefinedBy(_) => true, // Element<'a>
3050
3051            Type::TypeRef(name) => {
3052                let type_name = to_pascal_case(name);
3053
3054                // Check if this type requires a lifetime parameter (from config).
3055                // Does not require the type to be in the IMPORTS section.
3056                if self.config.imported_type_lifetimes.contains_key(name) {
3057                    return true;
3058                }
3059
3060                // Check if this is a locally-generated type that has a lifetime
3061                if self.types_with_lifetimes.contains(&type_name) {
3062                    return true;
3063                }
3064
3065                false
3066            }
3067            Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => self.type_needs_lifetime(inner),
3068            Type::Tagged { inner, .. }
3069            | Type::Constrained {
3070                base_type: inner, ..
3071            } => self.type_needs_lifetime(inner),
3072            Type::Sequence(fields) => fields.iter().any(|f| self.type_needs_lifetime(&f.ty)),
3073            Type::Set(fields) => fields.iter().any(|f| self.type_needs_lifetime(&f.ty)),
3074            Type::Choice(variants) => variants.iter().any(|v| self.type_needs_lifetime(&v.ty)),
3075            _ => false,
3076        }
3077    }
3078
3079    /// Check if a sequence type needs a lifetime parameter
3080    fn sequence_needs_lifetime(&self, fields: &[SequenceField]) -> bool {
3081        fields.iter().any(|field| {
3082            let field_name = to_snake_case(&field.name);
3083            self.config.raw_der_fields.contains(&field_name) || self.type_needs_lifetime(&field.ty)
3084        })
3085    }
3086
3087    /// Check if a choice type needs a lifetime parameter
3088    fn choice_needs_lifetime(&self, variants: &[ChoiceVariant]) -> bool {
3089        // Delegate uniformly to type_needs_lifetime, which recurses through Tagged
3090        // wrappers (both Explicit and Implicit) to check the inner type.
3091        // The previous early-return for IMPLICIT context tags was incorrect: it
3092        // prevented lifetime propagation for e.g. `[0] V2Form` where V2Form<'a>
3093        // requires a lifetime even though the outer tag is IMPLICIT.
3094        variants
3095            .iter()
3096            .any(|variant| self.type_needs_lifetime(&variant.ty))
3097    }
3098
3099    /// Generate a type alias declaration, adding lifetime parameter if needed
3100    fn generate_type_alias(
3101        &mut self,
3102        type_name: &str,
3103        rust_type: &str,
3104        asn1_type: &Type,
3105    ) -> Result<(), std::fmt::Error> {
3106        // Check if the type needs a lifetime by analyzing the ASN.1 type structure.
3107        // This is more reliable than checking the rust_type string, especially for
3108        // forward references where the RHS type may not yet be in types_with_lifetimes.
3109        let needs_lifetime = self.type_needs_lifetime(asn1_type);
3110
3111        if needs_lifetime {
3112            writeln!(
3113                &mut self.output,
3114                "pub type {}<'a> = {};",
3115                type_name, rust_type
3116            )?;
3117            // Track that this type alias has a lifetime parameter so that
3118            // later references to it will include the lifetime
3119            self.types_with_lifetimes.insert(type_name.to_string());
3120        } else {
3121            writeln!(&mut self.output, "pub type {} = {};", type_name, rust_type)?;
3122        }
3123        Ok(())
3124    }
3125
3126    /// Build OID registry from value assignments for resolving named references
3127    fn build_oid_registry(
3128        &self,
3129        values: &[crate::ast::ValueAssignment],
3130    ) -> std::collections::HashMap<String, Vec<u32>> {
3131        use std::collections::HashMap;
3132        let mut registry: HashMap<String, Vec<u32>> = HashMap::new();
3133
3134        // Iterate multiple times to resolve dependencies
3135        let mut changed = true;
3136        while changed {
3137            changed = false;
3138            for value_assignment in values {
3139                if registry.contains_key(&value_assignment.name) {
3140                    continue;
3141                }
3142
3143                if let crate::ast::Value::ObjectIdentifier(components) = &value_assignment.value {
3144                    let mut resolved = Vec::new();
3145                    let mut can_resolve = true;
3146
3147                    for component in components {
3148                        match component {
3149                            crate::ast::OidComponent::Number(n) => {
3150                                resolved.push(*n);
3151                            }
3152                            crate::ast::OidComponent::NamedRef(name) => {
3153                                if let Some(base_oid) = registry.get(name) {
3154                                    resolved.extend_from_slice(base_oid);
3155                                } else {
3156                                    can_resolve = false;
3157                                    break;
3158                                }
3159                            }
3160                        }
3161                    }
3162
3163                    if can_resolve {
3164                        registry.insert(value_assignment.name.clone(), resolved);
3165                        changed = true;
3166                    }
3167                }
3168            }
3169        }
3170
3171        registry
3172    }
3173
3174    /// Generate a value assignment as a constant
3175    fn generate_value_assignment(
3176        &mut self,
3177        value_assignment: &crate::ast::ValueAssignment,
3178        oid_registry: &std::collections::HashMap<String, Vec<u32>>,
3179    ) -> Result<(), std::fmt::Error> {
3180        let const_name = to_screaming_snake_case(&value_assignment.name);
3181
3182        match &value_assignment.value {
3183            crate::ast::Value::ObjectIdentifier(_components) => {
3184                // Look up the resolved OID from the registry
3185                if let Some(oid_values) = oid_registry.get(&value_assignment.name) {
3186                    // Generate constant
3187                    write!(&mut self.output, "pub const {}: &[u32] = &[", const_name)?;
3188                    for (i, value) in oid_values.iter().enumerate() {
3189                        if i > 0 {
3190                            write!(&mut self.output, ", ")?;
3191                        }
3192                        write!(&mut self.output, "{}", value)?;
3193                    }
3194                    writeln!(&mut self.output, "];")?;
3195                } else {
3196                    // OID couldn't be fully resolved - skip it
3197                    writeln!(
3198                        &mut self.output,
3199                        "// Note: Could not resolve OID for {}",
3200                        value_assignment.name
3201                    )?;
3202                }
3203            }
3204            crate::ast::Value::Integer(n) => {
3205                writeln!(&mut self.output, "pub const {}: i64 = {};", const_name, n)?;
3206            }
3207            crate::ast::Value::Boolean(b) => {
3208                writeln!(&mut self.output, "pub const {}: bool = {};", const_name, b)?;
3209            }
3210            crate::ast::Value::String(s) => {
3211                writeln!(
3212                    &mut self.output,
3213                    "pub const {}: &str = \"{}\";",
3214                    const_name, s
3215                )?;
3216            }
3217        }
3218
3219        Ok(())
3220    }
3221
3222    /// Pre-scan all type definitions to determine which need lifetimes
3223    /// This handles forward references by iterating until a fixed point
3224    fn prescan_types_for_lifetimes(&mut self, definitions: &[Definition]) {
3225        // Iterate until we reach a fixed point (no new types with lifetimes discovered)
3226        let mut changed = true;
3227        while changed {
3228            changed = false;
3229
3230            for def in definitions {
3231                // Skip if already in the set or should be skipped
3232                let type_name = to_pascal_case(&def.name);
3233                if self.types_with_lifetimes.contains(&type_name) {
3234                    continue;
3235                }
3236                if self.config.skip_imported_types.contains(&def.name) {
3237                    continue;
3238                }
3239
3240                // Check if this type needs a lifetime
3241                let needs_lifetime = match &def.ty {
3242                    Type::Sequence(fields) => self.sequence_needs_lifetime(fields),
3243                    Type::Set(fields) => self.sequence_needs_lifetime(fields),
3244                    Type::Choice(variants) => self.choice_needs_lifetime(variants),
3245                    // Special case: BitString/OctetString with NamedBitList generates owned types (no lifetime)
3246                    Type::Constrained {
3247                        base_type: _,
3248                        constraint,
3249                    } => match constraint.spec {
3250                        ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(_)) => false,
3251                        ConstraintSpec::Subtype(SubtypeConstraint::Intersection(
3252                            ref constraints,
3253                        )) if constraints
3254                            .iter()
3255                            .any(|c| matches!(c, SubtypeConstraint::NamedBitList(_))) =>
3256                        {
3257                            false
3258                        }
3259                        _ => self.type_needs_lifetime(&def.ty),
3260                    },
3261                    // For all other types, use the general type_needs_lifetime check
3262                    other => self.type_needs_lifetime(other),
3263                };
3264
3265                if needs_lifetime {
3266                    self.types_with_lifetimes.insert(type_name);
3267                    changed = true;
3268                }
3269            }
3270        }
3271    }
3272
3273    /// Recursively inline SEQUENCE OF and SET OF types for CHOICE variants and struct fields
3274    /// This enables the derive macro to detect Vec<T<'a>> patterns even when nested or tagged
3275    fn inline_sequence_of_types(&self, ty: &Type) -> String {
3276        match ty {
3277            Type::TypeRef(type_ref_name) => {
3278                let type_name = to_pascal_case(type_ref_name);
3279                if let Some(def_type) = self.type_definitions.get(&type_name) {
3280                    match def_type {
3281                        Type::SequenceOf(inner, _) => {
3282                            // Recursively inline the inner type in case it's also a SequenceOf/SetOf
3283                            let inner_rust_type = self.inline_sequence_of_types(inner);
3284                            format!("Vec<{}>", inner_rust_type)
3285                        }
3286                        Type::SetOf(inner, _) => {
3287                            // Recursively inline the inner type in case it's also a SetOf
3288                            let inner_rust_type = self.inline_sequence_of_types(inner);
3289                            format!("SetOf<{}>", inner_rust_type)
3290                        }
3291                        _ => self.rust_type(ty),
3292                    }
3293                } else {
3294                    self.rust_type(ty)
3295                }
3296            }
3297            // For tagged types, inline the inner type
3298            Type::Tagged { inner, .. } => self.inline_sequence_of_types(inner),
3299            // For constrained types, inline the base type
3300            Type::Constrained { base_type, .. } => self.inline_sequence_of_types(base_type),
3301            _ => self.rust_type(ty),
3302        }
3303    }
3304}
3305
3306impl Default for CodeGenerator {
3307    fn default() -> Self {
3308        Self::new()
3309    }
3310}
3311
3312/// Return the anonymous inner type if `ty` (after stripping exactly one layer
3313/// of `Tagged` or `Constrained`) is a bare SEQUENCE, SET, or CHOICE body.
3314///
3315/// Named `TypeRef`s are excluded — they are already concrete Rust types.
3316/// Only returns `Some` when the body is an *inline* anonymous structural type
3317/// that has not yet been given a name and cannot be referenced as-is.
3318fn anonymous_inner_type(ty: &Type) -> Option<&Type> {
3319    let candidate = match ty {
3320        Type::Tagged { inner, .. } => inner.as_ref(),
3321        Type::Constrained { base_type, .. } => base_type.as_ref(),
3322        other => other,
3323    };
3324    matches!(
3325        candidate,
3326        Type::Sequence(_) | Type::Set(_) | Type::Choice(_)
3327    )
3328    .then_some(candidate)
3329}
3330
3331/// Generate Rust code from an ASN.1 module using default configuration.
3332///
3333/// All string and binary types are emitted as owned heap-allocating types
3334/// (e.g. `OctetString`, `BitString`).  Use [`generate_with_config`] together
3335/// with [`CodeGenConfig`] to customise the output (e.g. to switch to
3336/// zero-copy borrowed types via [`StringTypeMode::Borrowed`]).
3337pub fn generate(module: &Module) -> Result<String, std::fmt::Error> {
3338    let mut gen = CodeGenerator::new();
3339    gen.generate_module(module)
3340}
3341
3342/// Generate Rust code from an ASN.1 module with custom configuration.
3343///
3344/// # Configuration options
3345///
3346/// - **`string_type_mode`** — choose between owned heap-allocating types
3347///   (`OctetString`, `BitString`, …) and zero-copy borrowed types
3348///   (`OctetStringRef<'a>`, `BitStringRef<'a>`, …).  See [`StringTypeMode`].
3349/// - **`module_path_prefix`** — emit `use <prefix>::<module>::Type;` statements
3350///   instead of comment-only import annotations.  See
3351///   [`CodeGenConfig::with_crate_imports`], [`CodeGenConfig::with_super_imports`],
3352///   [`CodeGenConfig::with_custom_prefix`].
3353/// - **`use_core`** — emit `core::convert::TryFrom` instead of
3354///   `std::convert::TryFrom` for `#![no_std]` environments.
3355/// - **`skip_imported_types`** / **`imported_type_lifetimes`** — fine-grained
3356///   control over which imported types are emitted and which carry a lifetime.
3357pub fn generate_with_config(
3358    module: &Module,
3359    config: CodeGenConfig,
3360) -> Result<String, std::fmt::Error> {
3361    let mut gen = CodeGenerator::with_config(config);
3362    gen.generate_module(module)
3363}