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        // Derive Default when every field is optional with no explicit ASN.1 DEFAULT —
2110        // in that case all fields map to None, which is always the correct default.
2111        let all_optional = fields.iter().all(|f| f.optional && f.default.is_none());
2112        if all_optional {
2113            writeln!(
2114                &mut self.output,
2115                "#[derive(Debug, Clone, PartialEq, Default)]"
2116            )?;
2117        } else {
2118            writeln!(&mut self.output, "#[derive(Debug, Clone, PartialEq)]")?;
2119        }
2120        let attr = self.derive_cfg_attr("derive(Asn1Sequence)");
2121        writeln!(&mut self.output, "{}", attr)?;
2122
2123        // Add lifetime parameter if any field needs it
2124        let needs_lifetime = self.sequence_needs_lifetime(fields);
2125        if needs_lifetime {
2126            writeln!(&mut self.output, "pub struct {}<'a> {{", name)?;
2127            // Track that this type has a lifetime
2128            self.types_with_lifetimes.insert(name.to_string());
2129        } else {
2130            writeln!(&mut self.output, "pub struct {} {{", name)?;
2131        }
2132
2133        for field in fields {
2134            self.generate_field(field)?;
2135        }
2136
2137        writeln!(&mut self.output, "}}")?;
2138
2139        self.generate_format_asn1_impl(name, needs_lifetime)?;
2140
2141        Ok(())
2142    }
2143
2144    fn generate_set_type(
2145        &mut self,
2146        name: &str,
2147        fields: &[SequenceField],
2148    ) -> Result<(), std::fmt::Error> {
2149        writeln!(&mut self.output, "#[derive(Debug, Clone, PartialEq)]")?;
2150        let attr = self.derive_cfg_attr("derive(Asn1Set)");
2151        writeln!(&mut self.output, "{}", attr)?;
2152
2153        // Add lifetime parameter if any field needs it
2154        let needs_lifetime = self.sequence_needs_lifetime(fields);
2155        if needs_lifetime {
2156            writeln!(&mut self.output, "pub struct {}<'a> {{", name)?;
2157            // Track that this type has a lifetime
2158            self.types_with_lifetimes.insert(name.to_string());
2159        } else {
2160            writeln!(&mut self.output, "pub struct {} {{", name)?;
2161        }
2162
2163        for field in fields {
2164            self.generate_field(field)?;
2165        }
2166
2167        writeln!(&mut self.output, "}}")?;
2168
2169        self.generate_format_asn1_impl(name, needs_lifetime)?;
2170
2171        Ok(())
2172    }
2173
2174    fn generate_choice_type(
2175        &mut self,
2176        name: &str,
2177        variants: &[ChoiceVariant],
2178    ) -> Result<(), std::fmt::Error> {
2179        writeln!(&mut self.output, "#[derive(Debug, Clone, PartialEq)]")?;
2180        let attr = self.derive_cfg_attr("derive(Asn1Choice)");
2181        writeln!(&mut self.output, "{}", attr)?;
2182
2183        // Add lifetime parameter if any variant needs it
2184        let needs_lifetime = self.choice_needs_lifetime(variants);
2185        if needs_lifetime {
2186            writeln!(&mut self.output, "pub enum {}<'a> {{", name)?;
2187            // Track that this type has a lifetime
2188            self.types_with_lifetimes.insert(name.to_string());
2189        } else {
2190            writeln!(&mut self.output, "pub enum {} {{", name)?;
2191        }
2192
2193        for variant in variants {
2194            let variant_name = to_pascal_case(&variant.name);
2195
2196            // For IMPLICIT context-specific tagged variants, force owned string types.
2197            // IMPLICIT tags require TLV reconstruction during decode (the context tag
2198            // replaces the inner type's own tag), so borrowed string types cannot borrow
2199            // from the original input — the reconstruction buffer is a local allocation.
2200            let rust_type = if let Type::Tagged { tag, .. } = &variant.ty {
2201                if tag.class == TagClass::ContextSpecific && tag.tagging == Tagging::Implicit {
2202                    let saved = self.config.string_type_mode.clone();
2203                    self.config.string_type_mode = StringTypeMode::Owned;
2204                    let ty = self.inline_sequence_of_types(&variant.ty);
2205                    self.config.string_type_mode = saved;
2206                    ty
2207                } else {
2208                    self.inline_sequence_of_types(&variant.ty)
2209                }
2210            } else {
2211                self.inline_sequence_of_types(&variant.ty)
2212            };
2213
2214            // If it's a tagged type, emit the attribute with the correct tagging mode
2215            if let Type::Tagged { tag, .. } = &variant.ty {
2216                match tag.class {
2217                    TagClass::ContextSpecific => {
2218                        let tagging_str = match tag.tagging {
2219                            Tagging::Explicit => "explicit",
2220                            Tagging::Implicit => "implicit",
2221                        };
2222                        let attr = self.field_derive_cfg_attr(&format!(
2223                            "asn1(tag({}, {}))",
2224                            tag.number, tagging_str
2225                        ));
2226                        writeln!(&mut self.output, "{}", attr)?;
2227                    }
2228                    TagClass::Application => {
2229                        writeln!(
2230                            &mut self.output,
2231                            "    // APPLICATION [{}] -- use asn1(application_tag) when supported",
2232                            tag.number
2233                        )?;
2234                        let attr = self
2235                            .field_derive_cfg_attr(&format!("asn1(tag({}, explicit))", tag.number));
2236                        writeln!(&mut self.output, "{}", attr)?;
2237                    }
2238                    TagClass::Universal => {
2239                        writeln!(&mut self.output, "    // UNIVERSAL [{}]", tag.number)?;
2240                    }
2241                    TagClass::Private => {
2242                        writeln!(&mut self.output, "    // PRIVATE [{}]", tag.number)?;
2243                    }
2244                }
2245            }
2246
2247            writeln!(&mut self.output, "    {}({}),", variant_name, rust_type)?;
2248        }
2249
2250        writeln!(&mut self.output, "}}")?;
2251
2252        self.generate_format_asn1_impl(name, needs_lifetime)?;
2253
2254        Ok(())
2255    }
2256
2257    fn generate_sequence_of_type(
2258        &mut self,
2259        name: &str,
2260        inner: &Type,
2261        size_constraint: Option<&SizeConstraint>,
2262    ) -> Result<(), std::fmt::Error> {
2263        // Handle anonymous inner types: SEQUENCE OF SEQUENCE { ... } / SET { ... } / CHOICE { ... }
2264        // Generate a named element type so the collection has a concrete Rust type to reference.
2265        if matches!(inner, Type::Sequence(_) | Type::Set(_) | Type::Choice(_)) {
2266            let element_name = format!("{}Element", name);
2267            let element_def = Definition {
2268                name: element_name.clone(),
2269                ty: inner.clone(),
2270            };
2271            self.generate_definition(&element_def)?;
2272            writeln!(&mut self.output)?;
2273            if let Some(constraint) = size_constraint {
2274                let constraint_str = self.format_size_constraint(constraint);
2275                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2276            }
2277            let rust_type = format!("Vec<{}>", element_name);
2278            // Construct the SequenceOf type for lifetime tracking
2279            let seq_of_type = Type::SequenceOf(Box::new(inner.clone()), size_constraint.cloned());
2280            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2281            return Ok(());
2282        }
2283
2284        // Handle inner type constraints (Phase 3)
2285        if let Type::Constrained {
2286            base_type,
2287            constraint,
2288        } = inner
2289        {
2290            // Generate element newtype with constrained validation
2291            let element_name = format!("{}Element", name);
2292
2293            // Generate the constrained element type
2294            match base_type.as_ref() {
2295                Type::Integer(_, _) => {
2296                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2297                        self.generate_constrained_integer(&element_name, subtype)?;
2298                    }
2299                }
2300                Type::IA5String(_)
2301                | Type::Utf8String(_)
2302                | Type::PrintableString(_)
2303                | Type::TeletexString(_)
2304                | Type::UniversalString(_)
2305                | Type::BmpString(_)
2306                | Type::GeneralString(_)
2307                | Type::NumericString(_)
2308                | Type::VisibleString(_) => {
2309                    let base_type_str = self.rust_type(base_type);
2310                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2311                        self.generate_constrained_string(&element_name, &base_type_str, subtype)?;
2312                    }
2313                }
2314                _ => {
2315                    // For other types, fall back to simple type alias
2316                    let inner_type = self.rust_type(inner);
2317                    writeln!(
2318                        &mut self.output,
2319                        "pub type {} = {};",
2320                        element_name, inner_type
2321                    )?;
2322                }
2323            }
2324
2325            writeln!(&mut self.output)?;
2326
2327            // Generate the collection type using the element type
2328            if let Some(constraint) = size_constraint {
2329                let constraint_str = self.format_size_constraint(constraint);
2330                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2331            }
2332            writeln!(
2333                &mut self.output,
2334                "pub type {} = Vec<{}>;",
2335                name, element_name
2336            )?;
2337        } else {
2338            // No constraint on inner type - generate simple type alias
2339            if let Some(constraint) = size_constraint {
2340                let constraint_str = self.format_size_constraint(constraint);
2341                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2342            }
2343            let inner_type = self.rust_type(inner);
2344            let rust_type = format!("Vec<{}>", inner_type);
2345            // Construct the SequenceOf/SetOf type for lifetime tracking
2346            let seq_of_type = Type::SequenceOf(Box::new(inner.clone()), size_constraint.cloned());
2347            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2348        }
2349        Ok(())
2350    }
2351
2352    fn generate_set_of_type(
2353        &mut self,
2354        name: &str,
2355        inner: &Type,
2356        size_constraint: Option<&SizeConstraint>,
2357    ) -> Result<(), std::fmt::Error> {
2358        // Handle anonymous inner types: SET OF SEQUENCE { ... } / SET { ... } / CHOICE { ... }
2359        if matches!(inner, Type::Sequence(_) | Type::Set(_) | Type::Choice(_)) {
2360            let element_name = format!("{}Element", name);
2361            let element_def = Definition {
2362                name: element_name.clone(),
2363                ty: inner.clone(),
2364            };
2365            self.generate_definition(&element_def)?;
2366            writeln!(&mut self.output)?;
2367            if let Some(constraint) = size_constraint {
2368                let constraint_str = self.format_size_constraint(constraint);
2369                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2370            }
2371            let rust_type = format!("SetOf<{}>", element_name);
2372            // Construct the SetOf type for lifetime tracking
2373            let seq_of_type = Type::SetOf(Box::new(inner.clone()), size_constraint.cloned());
2374            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2375            return Ok(());
2376        }
2377
2378        // Handle inner type constraints (Phase 3) - same as SEQUENCE OF
2379        if let Type::Constrained {
2380            base_type,
2381            constraint,
2382        } = inner
2383        {
2384            // Generate element newtype with constrained validation
2385            let element_name = format!("{}Element", name);
2386
2387            // Generate the constrained element type
2388            match base_type.as_ref() {
2389                Type::Integer(_, _) => {
2390                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2391                        self.generate_constrained_integer(&element_name, subtype)?;
2392                    }
2393                }
2394                Type::IA5String(_)
2395                | Type::Utf8String(_)
2396                | Type::PrintableString(_)
2397                | Type::TeletexString(_)
2398                | Type::UniversalString(_)
2399                | Type::BmpString(_)
2400                | Type::GeneralString(_)
2401                | Type::NumericString(_)
2402                | Type::VisibleString(_) => {
2403                    let base_type_str = self.rust_type(base_type);
2404                    if let ConstraintSpec::Subtype(ref subtype) = constraint.spec {
2405                        self.generate_constrained_string(&element_name, &base_type_str, subtype)?;
2406                    }
2407                }
2408                _ => {
2409                    // For other types, fall back to simple type alias
2410                    let inner_type = self.rust_type(inner);
2411                    writeln!(
2412                        &mut self.output,
2413                        "pub type {} = {};",
2414                        element_name, inner_type
2415                    )?;
2416                }
2417            }
2418
2419            writeln!(&mut self.output)?;
2420
2421            // Generate the collection type using the element type
2422            if let Some(constraint) = size_constraint {
2423                let constraint_str = self.format_size_constraint(constraint);
2424                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2425            }
2426            writeln!(
2427                &mut self.output,
2428                "pub type {} = SetOf<{}>;",
2429                name, element_name
2430            )?;
2431        } else {
2432            // No constraint on inner type - generate simple type alias
2433            if let Some(constraint) = size_constraint {
2434                let constraint_str = self.format_size_constraint(constraint);
2435                writeln!(&mut self.output, "// Constraint: {}", constraint_str)?;
2436            }
2437            let inner_type = self.rust_type(inner);
2438            let rust_type = format!("SetOf<{}>", inner_type);
2439            // Construct the SetOf type for lifetime tracking
2440            let seq_of_type = Type::SetOf(Box::new(inner.clone()), size_constraint.cloned());
2441            self.generate_type_alias(name, &rust_type, &seq_of_type)?;
2442        }
2443        Ok(())
2444    }
2445
2446    /// Return `true` when `ty` resolves to `Type::Any` or `Type::AnyDefinedBy`,
2447    /// following up to one level of TypeRef indirection through `type_definitions`.
2448    fn resolves_to_any(&self, ty: &Type) -> bool {
2449        match ty {
2450            Type::Any | Type::AnyDefinedBy(_) => true,
2451            Type::TypeRef(name) => {
2452                let clean = name.trim_end_matches("{}");
2453                matches!(
2454                    self.type_definitions.get(clean),
2455                    Some(Type::Any) | Some(Type::AnyDefinedBy(_))
2456                )
2457            }
2458            _ => false,
2459        }
2460    }
2461
2462    fn generate_field(&mut self, field: &SequenceField) -> Result<(), std::fmt::Error> {
2463        let field_name = to_snake_case(&field.name);
2464        // Inline SEQUENCE OF and SET OF types to enable derive macro Vec<T<'a>> detection
2465        let rust_type = self.inline_sequence_of_types(&field.ty);
2466
2467        // Add attribute for tagged fields
2468        if let Type::Tagged { tag, inner } = &field.ty {
2469            let tagging = match tag.tagging {
2470                Tagging::Explicit => "explicit",
2471                Tagging::Implicit => "implicit",
2472            };
2473            match tag.class {
2474                TagClass::ContextSpecific => {
2475                    let attr = self
2476                        .field_derive_cfg_attr(&format!("asn1(tag({}, {}))", tag.number, tagging));
2477                    writeln!(&mut self.output, "{}", attr)?;
2478                    // When any_as_raw_der = true, IMPLICIT-tagged ANY fields are emitted as
2479                    // RawDer<'a>.  The type alias (e.g. `CertificateSet<'a> = RawDer<'a>`) is
2480                    // opaque to the derive macro, which cannot detect that the aliased type is
2481                    // RawDer and therefore cannot select the correct encode path.  Emit a
2482                    // `rawder` marker attribute so the derive's encode path can use the direct
2483                    // content-write approach instead of the "encode then strip" approach.
2484                    if self.config.any_as_raw_der
2485                        && tag.tagging == Tagging::Implicit
2486                        && self.resolves_to_any(inner)
2487                    {
2488                        let rawder_attr = self.field_derive_cfg_attr("asn1(rawder)");
2489                        writeln!(&mut self.output, "{}", rawder_attr)?;
2490                    }
2491                }
2492                TagClass::Application => {
2493                    writeln!(
2494                        &mut self.output,
2495                        "    // APPLICATION [{} {}] -- use asn1(application_tag) when supported",
2496                        tag.number, tagging
2497                    )?;
2498                    let attr = self
2499                        .field_derive_cfg_attr(&format!("asn1(tag({}, {}))", tag.number, tagging));
2500                    writeln!(&mut self.output, "{}", attr)?;
2501                }
2502                TagClass::Universal => {
2503                    writeln!(
2504                        &mut self.output,
2505                        "    // UNIVERSAL [{}] {}",
2506                        tag.number, tagging
2507                    )?;
2508                }
2509                TagClass::Private => {
2510                    writeln!(
2511                        &mut self.output,
2512                        "    // PRIVATE [{}] {}",
2513                        tag.number, tagging
2514                    )?;
2515                }
2516            }
2517        }
2518
2519        // Add optional attribute
2520        // Fields with DEFAULT values are implicitly optional in ASN.1
2521        if field.optional || field.default.is_some() {
2522            let attr = self.field_derive_cfg_attr("asn1(optional)");
2523            writeln!(&mut self.output, "{}", attr)?;
2524        }
2525
2526        // Override with RawDer<'a> for fields listed in raw_der_fields.
2527        let rust_type = if self.config.raw_der_fields.contains(&field_name) {
2528            "RawDer<'a>".to_string()
2529        } else {
2530            rust_type
2531        };
2532
2533        // Fields are Option<T> if explicitly OPTIONAL or have DEFAULT values
2534        let final_type = if field.optional || field.default.is_some() {
2535            format!("Option<{}>", rust_type)
2536        } else {
2537            rust_type
2538        };
2539
2540        writeln!(&mut self.output, "    pub {}: {},", field_name, final_type)?;
2541
2542        Ok(())
2543    }
2544
2545    /// Generate a subtype definition (TypeRef with additional constraints)
2546    fn generate_subtype(
2547        &mut self,
2548        type_name: &str,
2549        base_type: &Type,
2550        constraint: &SubtypeConstraint,
2551    ) -> Result<(), std::fmt::Error> {
2552        let base_type_name = self.rust_type(base_type);
2553        let constraint_display = self.format_constraint_display(constraint);
2554
2555        // Generate doc comment
2556        writeln!(
2557            &mut self.output,
2558            "/// {} ({})",
2559            base_type_name, constraint_display
2560        )?;
2561
2562        // Generate newtype struct
2563        writeln!(
2564            &mut self.output,
2565            "#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]"
2566        )?;
2567        writeln!(
2568            &mut self.output,
2569            "pub struct {}({});",
2570            type_name, base_type_name
2571        )?;
2572        writeln!(&mut self.output)?;
2573
2574        writeln!(&mut self.output, "impl {} {{", type_name)?;
2575
2576        // Generate validation code
2577        // The approach: convert the base type to its underlying primitive, then validate
2578        let validation_code = self.generate_subtype_validation("val", constraint)?;
2579
2580        writeln!(
2581            &mut self.output,
2582            "    /// Create a new {} with validation",
2583            type_name
2584        )?;
2585        writeln!(
2586            &mut self.output,
2587            "    pub fn new(value: {}) -> Result<Self, &'static str> {{",
2588            base_type_name
2589        )?;
2590
2591        // Determine if this is an integer or string constraint
2592        let is_integer_constraint = matches!(
2593            constraint,
2594            SubtypeConstraint::SingleValue(_)
2595                | SubtypeConstraint::ValueRange { .. }
2596                | SubtypeConstraint::Union(_)
2597                | SubtypeConstraint::Intersection(_)
2598                | SubtypeConstraint::Complement(_)
2599        );
2600
2601        let description = self.generate_constraint_description(constraint);
2602
2603        if is_integer_constraint {
2604            writeln!(&mut self.output, "        let val = value.into_inner();")?;
2605            writeln!(&mut self.output, "        if {} {{", validation_code)?;
2606            writeln!(&mut self.output, "            Ok({}(value))", type_name)?;
2607            writeln!(&mut self.output, "        }} else {{")?;
2608            writeln!(&mut self.output, "            Err(\"{}\")", description)?;
2609            writeln!(&mut self.output, "        }}")?;
2610        } else {
2611            // String subtype - work with the string value directly
2612            let validation_code =
2613                self.generate_string_validation("value", &base_type_name, constraint);
2614            write!(&mut self.output, "{}", validation_code)?;
2615        }
2616
2617        writeln!(&mut self.output, "    }}")?;
2618        writeln!(&mut self.output)?;
2619
2620        // Generate unchecked constructor
2621        writeln!(
2622            &mut self.output,
2623            "    /// Create without validation (use with caution)"
2624        )?;
2625        writeln!(
2626            &mut self.output,
2627            "    pub const fn new_unchecked(value: {}) -> Self {{",
2628            base_type_name
2629        )?;
2630        writeln!(&mut self.output, "        {}(value)", type_name)?;
2631        writeln!(&mut self.output, "    }}")?;
2632        writeln!(&mut self.output)?;
2633
2634        // Generate get method
2635        writeln!(&mut self.output, "    /// Get the inner value")?;
2636        writeln!(
2637            &mut self.output,
2638            "    pub const fn get(&self) -> &{} {{",
2639            base_type_name
2640        )?;
2641        writeln!(&mut self.output, "        &self.0")?;
2642        writeln!(&mut self.output, "    }}")?;
2643        writeln!(&mut self.output)?;
2644
2645        // Generate into_inner method
2646        writeln!(
2647            &mut self.output,
2648            "    /// Consume and return the inner value"
2649        )?;
2650        writeln!(
2651            &mut self.output,
2652            "    pub fn into_inner(self) -> {} {{",
2653            base_type_name
2654        )?;
2655        writeln!(&mut self.output, "        self.0")?;
2656        writeln!(&mut self.output, "    }}")?;
2657
2658        writeln!(&mut self.output, "}}")?;
2659        writeln!(&mut self.output)?;
2660
2661        // Generate TryFrom impl
2662        let try_from_path = self.try_from_path();
2663        writeln!(
2664            &mut self.output,
2665            "impl {}::convert::TryFrom<{}> for {} {{",
2666            try_from_path, base_type_name, type_name
2667        )?;
2668        writeln!(&mut self.output, "    type Error = &'static str;")?;
2669        writeln!(&mut self.output)?;
2670        writeln!(
2671            &mut self.output,
2672            "    fn try_from(value: {}) -> Result<Self, Self::Error> {{",
2673            base_type_name
2674        )?;
2675        writeln!(&mut self.output, "        Self::new(value)")?;
2676        writeln!(&mut self.output, "    }}")?;
2677        writeln!(&mut self.output, "}}")?;
2678
2679        Ok(())
2680    }
2681
2682    /// Generate validation code for subtype constraints
2683    fn generate_subtype_validation(
2684        &mut self,
2685        var: &str,
2686        constraint: &SubtypeConstraint,
2687    ) -> Result<String, std::fmt::Error> {
2688        // Delegate to generate_constraint_validation which works with i64
2689        // This assumes the base type can be converted to Integer
2690        Ok(self.generate_constraint_validation(var, constraint))
2691    }
2692
2693    /// Emit `impl [<'a>] TypeName[<'a>] { pub fn format_asn1(...) }` after any
2694    /// generated struct/enum that implements `Encode`.
2695    ///
2696    /// The method encodes `self` to DER and delegates to
2697    /// `synta::format_asn1_bytes`, which supports both raw hex and
2698    /// human-readable ASN.1 text output.
2699    fn generate_format_asn1_impl(
2700        &mut self,
2701        name: &str,
2702        has_lifetime: bool,
2703    ) -> Result<(), std::fmt::Error> {
2704        writeln!(&mut self.output)?;
2705        if has_lifetime {
2706            writeln!(&mut self.output, "impl<'a> {}<'a> {{", name)?;
2707        } else {
2708            writeln!(&mut self.output, "impl {} {{", name)?;
2709        }
2710        writeln!(
2711            &mut self.output,
2712            "    /// Format the encoded DER bytes of this value."
2713        )?;
2714        writeln!(&mut self.output, "    ///")?;
2715        writeln!(
2716            &mut self.output,
2717            "    /// `mode` controls the output style:"
2718        )?;
2719        writeln!(
2720            &mut self.output,
2721            "    /// - [`synta::Asn1FormatMode::Hex`] — space-separated uppercase hex bytes"
2722        )?;
2723        writeln!(
2724            &mut self.output,
2725            "    /// - [`synta::Asn1FormatMode::Text`] — indented human-readable ASN.1 dump"
2726        )?;
2727        writeln!(
2728            &mut self.output,
2729            "    pub fn format_asn1(&self, mode: synta::Asn1FormatMode) -> String {{"
2730        )?;
2731        writeln!(&mut self.output, "        use synta::Encode;")?;
2732        writeln!(
2733            &mut self.output,
2734            "        let mut encoder = synta::Encoder::new(synta::Encoding::Der);"
2735        )?;
2736        writeln!(
2737            &mut self.output,
2738            "        if self.encode(&mut encoder).is_err() {{"
2739        )?;
2740        writeln!(
2741            &mut self.output,
2742            "            return String::from(\"<encode error>\");"
2743        )?;
2744        writeln!(&mut self.output, "        }}")?;
2745        writeln!(&mut self.output, "        match encoder.finish() {{")?;
2746        writeln!(
2747            &mut self.output,
2748            "            Ok(bytes) => synta::format_asn1_bytes(&bytes, mode),"
2749        )?;
2750        writeln!(
2751            &mut self.output,
2752            "            Err(_) => String::from(\"<encode error>\"),"
2753        )?;
2754        writeln!(&mut self.output, "        }}")?;
2755        writeln!(&mut self.output, "    }}")?;
2756        writeln!(&mut self.output, "}}")?;
2757        Ok(())
2758    }
2759
2760    /// Return the Rust type name for an ASN.1 string type, respecting the
2761    /// configured [`StringTypeMode`].
2762    ///
2763    /// Types that have a zero-copy `Ref` variant (e.g. `OctetString` /
2764    /// `OctetStringRef<'a>`) use `borrowed` in [`StringTypeMode::Borrowed`]
2765    /// mode. Types that only have an owned form (e.g. `TeletexString`) are
2766    /// returned as-is regardless of mode.
2767    #[inline]
2768    fn string_rust_type(&self, owned: &str, borrowed: &str) -> String {
2769        match self.config.string_type_mode {
2770            StringTypeMode::Owned => owned.to_string(),
2771            StringTypeMode::Borrowed => format!("{}<'a>", borrowed),
2772        }
2773    }
2774
2775    fn rust_type(&self, ty: &Type) -> String {
2776        match ty {
2777            Type::Integer(_, _) => "Integer".to_string(),
2778            Type::Enumerated(_) => "Enumerated".to_string(),
2779            Type::Real => "f64".to_string(),
2780            Type::Boolean => "Boolean".to_string(),
2781            // Types with both owned and zero-copy borrowed variants
2782            Type::OctetString(_) => self.string_rust_type("OctetString", "OctetStringRef"),
2783            Type::BitString(_) => self.string_rust_type("BitString", "BitStringRef"),
2784            Type::Utf8String(_) => self.string_rust_type("Utf8String", "Utf8StringRef"),
2785            Type::PrintableString(_) => {
2786                self.string_rust_type("PrintableString", "PrintableStringRef")
2787            }
2788            Type::IA5String(_) => self.string_rust_type("IA5String", "IA5StringRef"),
2789            Type::ObjectIdentifier => "ObjectIdentifier".to_string(),
2790            Type::Null => "Null".to_string(),
2791            // Types without a Ref variant — always owned
2792            Type::TeletexString(_) => "TeletexString".to_string(),
2793            Type::UniversalString(_) => "UniversalString".to_string(),
2794            Type::BmpString(_) => "BmpString".to_string(),
2795            Type::GeneralString(_) => "GeneralString".to_string(),
2796            Type::NumericString(_) => "NumericString".to_string(),
2797            Type::VisibleString(_) => "VisibleString".to_string(),
2798            Type::UtcTime => "UtcTime".to_string(),
2799            Type::GeneralizedTime => "GeneralizedTime".to_string(),
2800            Type::TypeRef(name) => {
2801                let type_name = to_pascal_case(name);
2802
2803                // Check if this type requires a specific lifetime parameter.
2804                // Applies to both explicitly imported types (IMPORTS section) and
2805                // bare TypeRefs that reference types from other schemas without a
2806                // formal IMPORTS declaration — `imported_types` membership is not
2807                // required for the lookup.
2808                if let Some(lifetime) = self.config.imported_type_lifetimes.get(name) {
2809                    return format!("{}<{}>", type_name, lifetime);
2810                }
2811
2812                // Check if this is a locally-generated type that has a lifetime
2813                if self.types_with_lifetimes.contains(&type_name) {
2814                    return format!("{}<'a>", type_name);
2815                }
2816
2817                type_name
2818            }
2819            Type::Class(_) => "/* class */".to_string(),
2820            Type::Sequence(_) => "/* nested sequence */".to_string(),
2821            Type::Set(_) => "/* nested set */".to_string(),
2822            Type::Choice(_) => "/* nested choice */".to_string(),
2823            Type::SequenceOf(inner, _) => format!("Vec<{}>", self.rust_type(inner)),
2824            Type::SetOf(inner, _) => format!("SetOf<{}>", self.rust_type(inner)),
2825            Type::Tagged { inner, .. } => self.rust_type(inner),
2826            Type::Constrained { base_type, .. } => self.rust_type(base_type),
2827            Type::Any => {
2828                if self.config.any_as_raw_der {
2829                    "RawDer<'a>".to_string()
2830                } else {
2831                    "Element<'a>".to_string()
2832                }
2833            }
2834            Type::AnyDefinedBy(_) => {
2835                if self.config.any_as_raw_der {
2836                    "RawDer<'a>".to_string()
2837                } else {
2838                    "Element<'a>".to_string()
2839                }
2840            }
2841        }
2842    }
2843
2844    /// Return `true` when `ty` requires a `'a` lifetime parameter.
2845    ///
2846    /// This is `true` when any of the following apply:
2847    /// - The type is `OctetString`, `BitString`, `Utf8String`, `PrintableString`,
2848    ///   or `IA5String` and [`StringTypeMode::Borrowed`] is active.
2849    /// - The type is `ANY` / `ANY DEFINED BY` (always `Element<'a>`).
2850    /// - The type is a reference to an imported type whose lifetime was declared
2851    ///   via [`CodeGenConfig::imported_type_lifetimes`].
2852    /// - The type is a reference to a locally-generated type that was previously
2853    ///   determined to need a lifetime (fixed-point prescan).
2854    /// - The type is a `SEQUENCE OF`, `SET OF`, tagged, or constrained wrapper
2855    ///   around a type that needs a lifetime.
2856    fn type_needs_lifetime(&self, ty: &Type) -> bool {
2857        match ty {
2858            // Types with Ref variants: need lifetime in Borrowed mode
2859            Type::OctetString(_)
2860            | Type::BitString(_)
2861            | Type::Utf8String(_)
2862            | Type::PrintableString(_)
2863            | Type::IA5String(_) => self.config.string_type_mode == StringTypeMode::Borrowed,
2864            Type::Any => true,             // Element<'a>
2865            Type::AnyDefinedBy(_) => true, // Element<'a>
2866
2867            Type::TypeRef(name) => {
2868                let type_name = to_pascal_case(name);
2869
2870                // Check if this type requires a lifetime parameter (from config).
2871                // Does not require the type to be in the IMPORTS section.
2872                if self.config.imported_type_lifetimes.contains_key(name) {
2873                    return true;
2874                }
2875
2876                // Check if this is a locally-generated type that has a lifetime
2877                if self.types_with_lifetimes.contains(&type_name) {
2878                    return true;
2879                }
2880
2881                false
2882            }
2883            Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => self.type_needs_lifetime(inner),
2884            Type::Tagged { inner, .. }
2885            | Type::Constrained {
2886                base_type: inner, ..
2887            } => self.type_needs_lifetime(inner),
2888            Type::Sequence(fields) => fields.iter().any(|f| self.type_needs_lifetime(&f.ty)),
2889            Type::Set(fields) => fields.iter().any(|f| self.type_needs_lifetime(&f.ty)),
2890            Type::Choice(variants) => variants.iter().any(|v| self.type_needs_lifetime(&v.ty)),
2891            _ => false,
2892        }
2893    }
2894
2895    /// Check if a sequence type needs a lifetime parameter
2896    fn sequence_needs_lifetime(&self, fields: &[SequenceField]) -> bool {
2897        fields.iter().any(|field| {
2898            let field_name = to_snake_case(&field.name);
2899            self.config.raw_der_fields.contains(&field_name) || self.type_needs_lifetime(&field.ty)
2900        })
2901    }
2902
2903    /// Check if a choice type needs a lifetime parameter
2904    fn choice_needs_lifetime(&self, variants: &[ChoiceVariant]) -> bool {
2905        variants.iter().any(|variant| {
2906            // IMPLICIT context-specific tagged variants use owned types (see generate_choice_type),
2907            // so they do not contribute a lifetime requirement to the enum.
2908            if let Type::Tagged { tag, .. } = &variant.ty {
2909                if tag.class == TagClass::ContextSpecific && tag.tagging == Tagging::Implicit {
2910                    return false;
2911                }
2912            }
2913            self.type_needs_lifetime(&variant.ty)
2914        })
2915    }
2916
2917    /// Generate a type alias declaration, adding lifetime parameter if needed
2918    fn generate_type_alias(
2919        &mut self,
2920        type_name: &str,
2921        rust_type: &str,
2922        asn1_type: &Type,
2923    ) -> Result<(), std::fmt::Error> {
2924        // Check if the type needs a lifetime by analyzing the ASN.1 type structure.
2925        // This is more reliable than checking the rust_type string, especially for
2926        // forward references where the RHS type may not yet be in types_with_lifetimes.
2927        let needs_lifetime = self.type_needs_lifetime(asn1_type);
2928
2929        if needs_lifetime {
2930            writeln!(
2931                &mut self.output,
2932                "pub type {}<'a> = {};",
2933                type_name, rust_type
2934            )?;
2935            // Track that this type alias has a lifetime parameter so that
2936            // later references to it will include the lifetime
2937            self.types_with_lifetimes.insert(type_name.to_string());
2938        } else {
2939            writeln!(&mut self.output, "pub type {} = {};", type_name, rust_type)?;
2940        }
2941        Ok(())
2942    }
2943
2944    /// Build OID registry from value assignments for resolving named references
2945    fn build_oid_registry(
2946        &self,
2947        values: &[crate::ast::ValueAssignment],
2948    ) -> std::collections::HashMap<String, Vec<u32>> {
2949        use std::collections::HashMap;
2950        let mut registry: HashMap<String, Vec<u32>> = HashMap::new();
2951
2952        // Iterate multiple times to resolve dependencies
2953        let mut changed = true;
2954        while changed {
2955            changed = false;
2956            for value_assignment in values {
2957                if registry.contains_key(&value_assignment.name) {
2958                    continue;
2959                }
2960
2961                if let crate::ast::Value::ObjectIdentifier(components) = &value_assignment.value {
2962                    let mut resolved = Vec::new();
2963                    let mut can_resolve = true;
2964
2965                    for component in components {
2966                        match component {
2967                            crate::ast::OidComponent::Number(n) => {
2968                                resolved.push(*n);
2969                            }
2970                            crate::ast::OidComponent::NamedRef(name) => {
2971                                if let Some(base_oid) = registry.get(name) {
2972                                    resolved.extend_from_slice(base_oid);
2973                                } else {
2974                                    can_resolve = false;
2975                                    break;
2976                                }
2977                            }
2978                        }
2979                    }
2980
2981                    if can_resolve {
2982                        registry.insert(value_assignment.name.clone(), resolved);
2983                        changed = true;
2984                    }
2985                }
2986            }
2987        }
2988
2989        registry
2990    }
2991
2992    /// Generate a value assignment as a constant
2993    fn generate_value_assignment(
2994        &mut self,
2995        value_assignment: &crate::ast::ValueAssignment,
2996        oid_registry: &std::collections::HashMap<String, Vec<u32>>,
2997    ) -> Result<(), std::fmt::Error> {
2998        let const_name = to_screaming_snake_case(&value_assignment.name);
2999
3000        match &value_assignment.value {
3001            crate::ast::Value::ObjectIdentifier(_components) => {
3002                // Look up the resolved OID from the registry
3003                if let Some(oid_values) = oid_registry.get(&value_assignment.name) {
3004                    // Generate constant
3005                    write!(&mut self.output, "pub const {}: &[u32] = &[", const_name)?;
3006                    for (i, value) in oid_values.iter().enumerate() {
3007                        if i > 0 {
3008                            write!(&mut self.output, ", ")?;
3009                        }
3010                        write!(&mut self.output, "{}", value)?;
3011                    }
3012                    writeln!(&mut self.output, "];")?;
3013                } else {
3014                    // OID couldn't be fully resolved - skip it
3015                    writeln!(
3016                        &mut self.output,
3017                        "// Note: Could not resolve OID for {}",
3018                        value_assignment.name
3019                    )?;
3020                }
3021            }
3022            crate::ast::Value::Integer(n) => {
3023                writeln!(&mut self.output, "pub const {}: i64 = {};", const_name, n)?;
3024            }
3025            crate::ast::Value::Boolean(b) => {
3026                writeln!(&mut self.output, "pub const {}: bool = {};", const_name, b)?;
3027            }
3028            crate::ast::Value::String(s) => {
3029                writeln!(
3030                    &mut self.output,
3031                    "pub const {}: &str = \"{}\";",
3032                    const_name, s
3033                )?;
3034            }
3035        }
3036
3037        Ok(())
3038    }
3039
3040    /// Pre-scan all type definitions to determine which need lifetimes
3041    /// This handles forward references by iterating until a fixed point
3042    fn prescan_types_for_lifetimes(&mut self, definitions: &[Definition]) {
3043        // Iterate until we reach a fixed point (no new types with lifetimes discovered)
3044        let mut changed = true;
3045        while changed {
3046            changed = false;
3047
3048            for def in definitions {
3049                // Skip if already in the set or should be skipped
3050                let type_name = to_pascal_case(&def.name);
3051                if self.types_with_lifetimes.contains(&type_name) {
3052                    continue;
3053                }
3054                if self.config.skip_imported_types.contains(&def.name) {
3055                    continue;
3056                }
3057
3058                // Check if this type needs a lifetime
3059                let needs_lifetime = match &def.ty {
3060                    Type::Sequence(fields) => self.sequence_needs_lifetime(fields),
3061                    Type::Set(fields) => self.sequence_needs_lifetime(fields),
3062                    Type::Choice(variants) => self.choice_needs_lifetime(variants),
3063                    // Special case: BitString/OctetString with NamedBitList generates owned types (no lifetime)
3064                    Type::Constrained {
3065                        base_type: _,
3066                        constraint,
3067                    } => match constraint.spec {
3068                        ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(_)) => false,
3069                        ConstraintSpec::Subtype(SubtypeConstraint::Intersection(
3070                            ref constraints,
3071                        )) if constraints
3072                            .iter()
3073                            .any(|c| matches!(c, SubtypeConstraint::NamedBitList(_))) =>
3074                        {
3075                            false
3076                        }
3077                        _ => self.type_needs_lifetime(&def.ty),
3078                    },
3079                    // For all other types, use the general type_needs_lifetime check
3080                    other => self.type_needs_lifetime(other),
3081                };
3082
3083                if needs_lifetime {
3084                    self.types_with_lifetimes.insert(type_name);
3085                    changed = true;
3086                }
3087            }
3088        }
3089    }
3090
3091    /// Recursively inline SEQUENCE OF and SET OF types for CHOICE variants and struct fields
3092    /// This enables the derive macro to detect Vec<T<'a>> patterns even when nested or tagged
3093    fn inline_sequence_of_types(&self, ty: &Type) -> String {
3094        match ty {
3095            Type::TypeRef(type_ref_name) => {
3096                let type_name = to_pascal_case(type_ref_name);
3097                if let Some(def_type) = self.type_definitions.get(&type_name) {
3098                    match def_type {
3099                        Type::SequenceOf(inner, _) => {
3100                            // Recursively inline the inner type in case it's also a SequenceOf/SetOf
3101                            let inner_rust_type = self.inline_sequence_of_types(inner);
3102                            format!("Vec<{}>", inner_rust_type)
3103                        }
3104                        Type::SetOf(inner, _) => {
3105                            // Recursively inline the inner type in case it's also a SetOf
3106                            let inner_rust_type = self.inline_sequence_of_types(inner);
3107                            format!("SetOf<{}>", inner_rust_type)
3108                        }
3109                        _ => self.rust_type(ty),
3110                    }
3111                } else {
3112                    self.rust_type(ty)
3113                }
3114            }
3115            // For tagged types, inline the inner type
3116            Type::Tagged { inner, .. } => self.inline_sequence_of_types(inner),
3117            // For constrained types, inline the base type
3118            Type::Constrained { base_type, .. } => self.inline_sequence_of_types(base_type),
3119            _ => self.rust_type(ty),
3120        }
3121    }
3122}
3123
3124impl Default for CodeGenerator {
3125    fn default() -> Self {
3126        Self::new()
3127    }
3128}
3129
3130/// Generate Rust code from an ASN.1 module using default configuration.
3131///
3132/// All string and binary types are emitted as owned heap-allocating types
3133/// (e.g. `OctetString`, `BitString`).  Use [`generate_with_config`] together
3134/// with [`CodeGenConfig`] to customise the output (e.g. to switch to
3135/// zero-copy borrowed types via [`StringTypeMode::Borrowed`]).
3136pub fn generate(module: &Module) -> Result<String, std::fmt::Error> {
3137    let mut gen = CodeGenerator::new();
3138    gen.generate_module(module)
3139}
3140
3141/// Generate Rust code from an ASN.1 module with custom configuration.
3142///
3143/// # Configuration options
3144///
3145/// - **`string_type_mode`** — choose between owned heap-allocating types
3146///   (`OctetString`, `BitString`, …) and zero-copy borrowed types
3147///   (`OctetStringRef<'a>`, `BitStringRef<'a>`, …).  See [`StringTypeMode`].
3148/// - **`module_path_prefix`** — emit `use <prefix>::<module>::Type;` statements
3149///   instead of comment-only import annotations.  See
3150///   [`CodeGenConfig::with_crate_imports`], [`CodeGenConfig::with_super_imports`],
3151///   [`CodeGenConfig::with_custom_prefix`].
3152/// - **`use_core`** — emit `core::convert::TryFrom` instead of
3153///   `std::convert::TryFrom` for `#![no_std]` environments.
3154/// - **`skip_imported_types`** / **`imported_type_lifetimes`** — fine-grained
3155///   control over which imported types are emitted and which carry a lifetime.
3156pub fn generate_with_config(
3157    module: &Module,
3158    config: CodeGenConfig,
3159) -> Result<String, std::fmt::Error> {
3160    let mut gen = CodeGenerator::with_config(config);
3161    gen.generate_module(module)
3162}