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