Skip to main content

ooxml_codegen/
parser_gen.rs

1//! Event-based XML parser generator.
2//!
3//! This module generates quick-xml event-based parsers for OOXML types,
4//! which are ~3x faster than serde-based deserialization.
5
6use crate::ast::{Pattern, QName, Schema};
7use crate::codegen::CodegenConfig;
8use std::collections::{HashMap, HashSet};
9use std::fmt::Write;
10
11/// Generate parser code for all types in the schema.
12pub fn generate_parsers(schema: &Schema, config: &CodegenConfig) -> String {
13    let mut g = ParserGenerator::new(schema, config);
14    g.run()
15}
16
17/// How to parse a field value from XML.
18#[derive(Debug, Clone, Copy, PartialEq)]
19enum ParseStrategy {
20    /// Call from_xml on a complex type (CT_* or EG_*)
21    FromXml,
22    /// Read text content and use FromStr (enums, numbers)
23    TextFromStr,
24    /// Read text content as String directly
25    TextString,
26    /// Read text content and decode as hex (Vec<u8>)
27    TextHexBinary,
28    /// Read text content and decode as base64 (Vec<u8>)
29    TextBase64Binary,
30}
31
32struct ParserGenerator<'a> {
33    schema: &'a Schema,
34    config: &'a CodegenConfig,
35    output: String,
36    definitions: HashMap<&'a str, &'a Pattern>,
37    /// Track generated Rust type names to avoid duplicate impl blocks from merged schemas.
38    generated_names: HashSet<String>,
39}
40
41impl<'a> ParserGenerator<'a> {
42    fn new(schema: &'a Schema, config: &'a CodegenConfig) -> Self {
43        let definitions: HashMap<&str, &Pattern> = schema
44            .definitions
45            .iter()
46            .map(|d| (d.name.as_str(), &d.pattern))
47            .collect();
48
49        Self {
50            schema,
51            config,
52            output: String::new(),
53            definitions,
54            generated_names: HashSet::new(),
55        }
56    }
57
58    fn run(&mut self) -> String {
59        self.write_header();
60
61        // Generate parsers for complex types only
62        for def in &self.schema.definitions {
63            if !def.name.contains("_ST_") && !self.is_simple_type(&def.pattern) {
64                // Skip inline attribute references (like r_id) - they're inlined into parent types
65                if self.is_inline_attribute_ref(&def.name, &def.pattern) {
66                    continue;
67                }
68                // Deduplicate by Rust type name to avoid duplicate impl blocks from merged schemas.
69                let rust_name = self.to_rust_type_name(&def.name);
70                if !self.generated_names.insert(rust_name) {
71                    continue;
72                }
73                if def.name.contains("_EG_") && self.is_element_choice(&def.pattern) {
74                    // Element group - generate enum parser
75                    if let Some(code) = self.gen_element_group_parser(def) {
76                        self.output.push_str(&code);
77                        self.output.push('\n');
78                    }
79                } else if !self.is_type_alias(&def.pattern) {
80                    // Complex type struct - generate struct parser (skip type aliases)
81                    if let Some(code) = self.gen_struct_parser(def) {
82                        self.output.push_str(&code);
83                        self.output.push('\n');
84                    }
85                }
86            }
87        }
88
89        std::mem::take(&mut self.output)
90    }
91
92    /// Check if a pattern would generate a type alias rather than a struct.
93    /// Type aliases are generated for:
94    /// - Pattern::Element { pattern } (element-only definitions)
95    /// - Pattern::Datatype (XSD type alias)
96    /// - Pattern::Ref to a simple type, datatype, or unknown definition
97    ///
98    /// We DON'T skip Ref patterns that point to attribute definitions,
99    /// because those generate structs that need FromXml impls.
100    fn is_type_alias(&self, pattern: &Pattern) -> bool {
101        match pattern {
102            // Element wrappers are always type aliases
103            Pattern::Element { .. } => true,
104            // Direct datatype references are type aliases
105            Pattern::Datatype { .. } => true,
106            // Refs need to be checked - only skip if they resolve to simple types
107            Pattern::Ref(name) => {
108                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
109                    // If it resolves to a simple type (choice of strings, datatype, etc.)
110                    self.is_simple_type(def_pattern)
111                        // Or to another type alias
112                        || self.is_type_alias(def_pattern)
113                } else {
114                    // Unknown ref (from another schema) - these generate empty structs
115                    // that still need FromXml impls
116                    false
117                }
118            }
119            _ => false,
120        }
121    }
122
123    /// Check if a definition is a pure attribute reference that should be inlined.
124    /// These are attribute patterns (like r_id, r_embed) that don't have CT_ in their name.
125    fn is_inline_attribute_ref(&self, name: &str, pattern: &Pattern) -> bool {
126        !name.contains("_CT_") && matches!(pattern, Pattern::Attribute { .. })
127    }
128
129    fn write_header(&mut self) {
130        writeln!(self.output, "// Event-based parsers for generated types.").unwrap();
131        writeln!(
132            self.output,
133            "// ~3x faster than serde-based deserialization."
134        )
135        .unwrap();
136        writeln!(self.output).unwrap();
137        writeln!(self.output, "#![allow(unused_variables)]").unwrap();
138        writeln!(self.output, "#![allow(unused_imports)]").unwrap();
139        writeln!(self.output, "#![allow(clippy::single_match)]").unwrap();
140        writeln!(self.output, "#![allow(clippy::match_single_binding)]").unwrap();
141        writeln!(self.output, "#![allow(clippy::manual_is_multiple_of)]").unwrap();
142        writeln!(self.output).unwrap();
143        writeln!(self.output, "use super::generated::*;").unwrap();
144        // Add cross-crate imports for types from other schemas
145        for import in &self.config.cross_crate_imports {
146            writeln!(self.output, "use {};", import).unwrap();
147        }
148        writeln!(self.output, "use quick_xml::Reader;").unwrap();
149        writeln!(self.output, "use quick_xml::events::{{Event, BytesStart}};").unwrap();
150        writeln!(self.output, "use std::io::BufRead;").unwrap();
151        // Import shared traits and error types from ooxml-xml
152        writeln!(self.output, "pub use ooxml_xml::{{FromXml, ParseError}};").unwrap();
153        writeln!(self.output, "#[cfg(feature = \"extra-children\")]").unwrap();
154        writeln!(
155            self.output,
156            "use ooxml_xml::{{PositionedNode, RawXmlElement, RawXmlNode}};"
157        )
158        .unwrap();
159        writeln!(self.output).unwrap();
160        // Add skip_element helper (allow dead_code since extra-children feature captures instead)
161        writeln!(self.output, "#[allow(dead_code)]").unwrap();
162        writeln!(self.output, "/// Skip an element and all its children.").unwrap();
163        writeln!(
164            self.output,
165            "fn skip_element<R: BufRead>(reader: &mut Reader<R>) -> Result<(), ParseError> {{"
166        )
167        .unwrap();
168        writeln!(self.output, "    let mut depth = 1u32;").unwrap();
169        writeln!(self.output, "    let mut buf = Vec::new();").unwrap();
170        writeln!(self.output, "    loop {{").unwrap();
171        writeln!(
172            self.output,
173            "        match reader.read_event_into(&mut buf)? {{"
174        )
175        .unwrap();
176        writeln!(self.output, "            Event::Start(_) => depth += 1,").unwrap();
177        writeln!(self.output, "            Event::End(_) => {{").unwrap();
178        writeln!(self.output, "                depth -= 1;").unwrap();
179        writeln!(self.output, "                if depth == 0 {{ break; }}").unwrap();
180        writeln!(self.output, "            }}").unwrap();
181        writeln!(self.output, "            Event::Eof => break,").unwrap();
182        writeln!(self.output, "            _ => {{}}").unwrap();
183        writeln!(self.output, "        }}").unwrap();
184        writeln!(self.output, "        buf.clear();").unwrap();
185        writeln!(self.output, "    }}").unwrap();
186        writeln!(self.output, "    Ok(())").unwrap();
187        writeln!(self.output, "}}").unwrap();
188        writeln!(self.output).unwrap();
189        // Add read_text_content helper for reading element text
190        writeln!(self.output, "#[allow(dead_code)]").unwrap();
191        writeln!(
192            self.output,
193            "/// Read the text content of an element until its end tag."
194        )
195        .unwrap();
196        writeln!(self.output, "fn read_text_content<R: BufRead>(reader: &mut Reader<R>) -> Result<String, ParseError> {{").unwrap();
197        writeln!(self.output, "    let mut text = String::new();").unwrap();
198        writeln!(self.output, "    let mut buf = Vec::new();").unwrap();
199        writeln!(self.output, "    loop {{").unwrap();
200        writeln!(
201            self.output,
202            "        match reader.read_event_into(&mut buf)? {{"
203        )
204        .unwrap();
205        writeln!(
206            self.output,
207            "            Event::Text(e) => text.push_str(&e.decode().unwrap_or_default()),"
208        )
209        .unwrap();
210        writeln!(
211            self.output,
212            "            Event::CData(e) => text.push_str(&e.decode().unwrap_or_default()),"
213        )
214        .unwrap();
215        writeln!(self.output, "            Event::End(_) => break,").unwrap();
216        writeln!(self.output, "            Event::Eof => break,").unwrap();
217        writeln!(self.output, "            _ => {{}}").unwrap();
218        writeln!(self.output, "        }}").unwrap();
219        writeln!(self.output, "        buf.clear();").unwrap();
220        writeln!(self.output, "    }}").unwrap();
221        writeln!(self.output, "    Ok(text)").unwrap();
222        writeln!(self.output, "}}").unwrap();
223        writeln!(self.output).unwrap();
224        // Add decode_hex helper for hexBinary types
225        writeln!(self.output, "#[allow(dead_code)]").unwrap();
226        writeln!(self.output, "/// Decode a hex string to bytes.").unwrap();
227        writeln!(self.output, "fn decode_hex(s: &str) -> Option<Vec<u8>> {{").unwrap();
228        writeln!(self.output, "    let s = s.trim();").unwrap();
229        writeln!(self.output, "    if s.len() % 2 != 0 {{ return None; }}").unwrap();
230        writeln!(self.output, "    (0..s.len())").unwrap();
231        writeln!(self.output, "        .step_by(2)").unwrap();
232        writeln!(
233            self.output,
234            "        .map(|i| u8::from_str_radix(&s[i..i + 2], 16).ok())"
235        )
236        .unwrap();
237        writeln!(self.output, "        .collect()").unwrap();
238        writeln!(self.output, "}}").unwrap();
239        writeln!(self.output).unwrap();
240
241        // Add decode_base64 helper for base64Binary types
242        writeln!(self.output, "#[allow(dead_code)]").unwrap();
243        writeln!(self.output, "/// Decode a base64 string to bytes.").unwrap();
244        writeln!(
245            self.output,
246            "fn decode_base64(s: &str) -> Option<Vec<u8>> {{"
247        )
248        .unwrap();
249        writeln!(self.output, "    use base64::Engine;").unwrap();
250        writeln!(
251            self.output,
252            "    base64::engine::general_purpose::STANDARD.decode(s.trim()).ok()"
253        )
254        .unwrap();
255        writeln!(self.output, "}}").unwrap();
256        writeln!(self.output).unwrap();
257    }
258
259    fn is_simple_type(&self, pattern: &Pattern) -> bool {
260        match pattern {
261            Pattern::Choice(variants) => variants
262                .iter()
263                .all(|v| matches!(v, Pattern::StringLiteral(_))),
264            Pattern::StringLiteral(_) => true,
265            Pattern::Datatype { .. } => true,
266            Pattern::List(_) => true,
267            Pattern::Ref(name) => self
268                .definitions
269                .get(name.as_str())
270                .is_some_and(|p| self.is_simple_type(p)),
271            _ => false,
272        }
273    }
274
275    fn is_element_choice(&self, pattern: &Pattern) -> bool {
276        match pattern {
277            Pattern::Choice(variants) => variants.iter().any(Self::is_direct_element_variant),
278            _ => false,
279        }
280    }
281
282    fn is_direct_element_variant(pattern: &Pattern) -> bool {
283        match pattern {
284            Pattern::Element { .. } => true,
285            Pattern::Optional(inner) | Pattern::ZeroOrMore(inner) | Pattern::OneOrMore(inner) => {
286                Self::is_direct_element_variant(inner)
287            }
288            _ => false,
289        }
290    }
291
292    /// Check if a pattern contains XML child elements (even from unresolved refs).
293    fn has_xml_children_pattern(&self, pattern: &Pattern) -> bool {
294        match pattern {
295            Pattern::Empty => false,
296            Pattern::Attribute { .. } => false,
297            Pattern::Element { .. } => true,
298            Pattern::Ref(name) => {
299                if name.contains("_AG_") {
300                    return false;
301                }
302                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
303                    self.has_xml_children_pattern(def_pattern)
304                } else {
305                    true
306                }
307            }
308            Pattern::Sequence(items) | Pattern::Interleave(items) | Pattern::Choice(items) => {
309                items.iter().any(|i| self.has_xml_children_pattern(i))
310            }
311            Pattern::Optional(inner)
312            | Pattern::ZeroOrMore(inner)
313            | Pattern::OneOrMore(inner)
314            | Pattern::Group(inner)
315            | Pattern::Mixed(inner) => self.has_xml_children_pattern(inner),
316            Pattern::Text => true,
317            _ => false,
318        }
319    }
320
321    /// Check if a pattern contains XML attributes (even from unresolved refs).
322    fn has_xml_attr_pattern(&self, pattern: &Pattern) -> bool {
323        match pattern {
324            Pattern::Attribute { .. } => true,
325            Pattern::Ref(name) if name.contains("_AG_") => true,
326            Pattern::Ref(name) => {
327                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
328                    self.has_xml_attr_pattern(def_pattern)
329                } else {
330                    false
331                }
332            }
333            Pattern::Sequence(items) | Pattern::Interleave(items) | Pattern::Choice(items) => {
334                items.iter().any(|i| self.has_xml_attr_pattern(i))
335            }
336            Pattern::Optional(inner)
337            | Pattern::ZeroOrMore(inner)
338            | Pattern::OneOrMore(inner)
339            | Pattern::Group(inner) => self.has_xml_attr_pattern(inner),
340            _ => false,
341        }
342    }
343
344    fn eg_ref_to_field_name(&self, name: &str) -> String {
345        let spec_name = strip_namespace_prefix(name);
346        let short = spec_name.strip_prefix("EG_").unwrap_or(spec_name);
347        // Check names.yaml field mapping first
348        if let Some(mappings) = &self.config.name_mappings
349            && let Some(mapped) = mappings.resolve_field(&self.config.module_name, short)
350        {
351            return mapped.to_string();
352        }
353        to_snake_case(short)
354    }
355
356    fn is_eg_content_field(&self, field: &Field) -> bool {
357        if let Pattern::Ref(name) = &field.pattern
358            && name.contains("_EG_")
359            && let Some(pattern) = self.definitions.get(name.as_str())
360        {
361            return self.is_element_choice(pattern);
362        }
363        false
364    }
365
366    /// Recursively collect all element XML local names from an EG_* definition.
367    fn collect_element_variant_names(
368        &self,
369        pattern: &Pattern,
370        names: &mut Vec<String>,
371        visited: &mut std::collections::HashSet<String>,
372    ) {
373        match pattern {
374            Pattern::Element { name, .. } => {
375                names.push(name.local.clone());
376            }
377            Pattern::Optional(inner)
378            | Pattern::ZeroOrMore(inner)
379            | Pattern::OneOrMore(inner)
380            | Pattern::Group(inner) => {
381                self.collect_element_variant_names(inner, names, visited);
382            }
383            Pattern::Ref(name) if name.contains("_EG_") && visited.insert(name.clone()) => {
384                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
385                    self.collect_element_variant_names(def_pattern, names, visited);
386                }
387            }
388            Pattern::Choice(items) | Pattern::Sequence(items) | Pattern::Interleave(items) => {
389                for item in items {
390                    self.collect_element_variant_names(item, names, visited);
391                }
392            }
393            _ => {}
394        }
395    }
396
397    /// Recursively collect element variants with their types for EG enum parsers.
398    /// Returns tuples of (xml_name, parse_type, needs_box, type_alias_has_box).
399    fn collect_eg_variants(
400        &self,
401        pattern: &Pattern,
402        variants: &mut Vec<(String, String, bool, bool)>,
403        visited: &mut std::collections::HashSet<String>,
404    ) {
405        match pattern {
406            Pattern::Element { name, pattern } => {
407                // Enum variants are not in Vec context, so may need boxing
408                let (rust_type, needs_box) = self.pattern_to_rust_type(pattern, false);
409                // For type aliases (Pattern::Element wrappers), resolve to the inner type
410                // that actually has a FromXml impl
411                let (actual_type, type_alias_has_box) =
412                    self.resolve_from_xml_type_with_box(pattern);
413                let inner_type = actual_type.unwrap_or(rust_type);
414                variants.push((
415                    name.local.clone(),
416                    inner_type,
417                    needs_box,
418                    type_alias_has_box,
419                ));
420            }
421            Pattern::Optional(inner)
422            | Pattern::ZeroOrMore(inner)
423            | Pattern::OneOrMore(inner)
424            | Pattern::Group(inner) => {
425                self.collect_eg_variants(inner, variants, visited);
426            }
427            Pattern::Ref(name) if name.contains("_EG_") && visited.insert(name.clone()) => {
428                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
429                    self.collect_eg_variants(def_pattern, variants, visited);
430                }
431            }
432            Pattern::Choice(items) | Pattern::Sequence(items) | Pattern::Interleave(items) => {
433                for item in items {
434                    self.collect_eg_variants(item, variants, visited);
435                }
436            }
437            _ => {}
438        }
439    }
440
441    fn gen_element_group_parser(&self, def: &crate::ast::Definition) -> Option<String> {
442        let rust_name = self.to_rust_type_name(&def.name);
443
444        let Pattern::Choice(variants) = &def.pattern else {
445            return None;
446        };
447
448        // Collect element variants recursively (follows nested EG_* refs)
449        let mut element_variants = Vec::new();
450        let mut visited = std::collections::HashSet::new();
451        visited.insert(def.name.clone());
452        for v in variants {
453            self.collect_eg_variants(v, &mut element_variants, &mut visited);
454        }
455        // Dedup by xml name
456        let mut seen = std::collections::HashSet::new();
457        element_variants.retain(|(name, _, _, _)| seen.insert(name.clone()));
458
459        if element_variants.is_empty() {
460            return None;
461        }
462
463        let mut code = String::new();
464        writeln!(code, "impl FromXml for {} {{", rust_name).unwrap();
465        writeln!(
466            code,
467            "    fn from_xml<R: BufRead>(reader: &mut Reader<R>, start_tag: &BytesStart, is_empty: bool) -> Result<Self, ParseError> {{"
468        )
469        .unwrap();
470        writeln!(code, "        let tag = start_tag.local_name();").unwrap();
471        writeln!(code, "        match tag.as_ref() {{").unwrap();
472
473        for (xml_name, inner_type, needs_box, type_alias_has_box) in &element_variants {
474            let variant_name = self.to_rust_variant_name(xml_name);
475            writeln!(code, "            b\"{}\" => {{", xml_name).unwrap();
476            writeln!(
477                code,
478                "                let inner = {}::from_xml(reader, start_tag, is_empty)?;",
479                inner_type
480            )
481            .unwrap();
482            // When the type alias already has Box (e.g., CTFoo = Box<Inner>),
483            // we need to wrap inner once for the alias, then again for the enum variant.
484            // When no type alias box, we just need one Box for the enum variant.
485            let wrap_expr = match (*needs_box, *type_alias_has_box) {
486                (true, true) => "Box::new(Box::new(inner))",
487                (true, false) => "Box::new(inner)",
488                (false, true) => "Box::new(inner)",
489                (false, false) => "inner",
490            };
491            writeln!(
492                code,
493                "                Ok(Self::{}({}))",
494                variant_name, wrap_expr
495            )
496            .unwrap();
497            writeln!(code, "            }}").unwrap();
498        }
499
500        writeln!(code, "            _ => Err(ParseError::UnexpectedElement(").unwrap();
501        writeln!(
502            code,
503            "                String::from_utf8_lossy(start_tag.name().as_ref()).into_owned()"
504        )
505        .unwrap();
506        writeln!(code, "            )),").unwrap();
507        writeln!(code, "        }}").unwrap();
508        writeln!(code, "    }}").unwrap();
509        writeln!(code, "}}").unwrap();
510
511        Some(code)
512    }
513
514    fn gen_struct_parser(&self, def: &crate::ast::Definition) -> Option<String> {
515        let rust_name = self.to_rust_type_name(&def.name);
516        let fields = self.extract_fields(&def.pattern);
517
518        if fields.is_empty() {
519            let has_unresolved_children = self.has_xml_children_pattern(&def.pattern);
520            let has_unresolved_attrs = self.has_xml_attr_pattern(&def.pattern);
521
522            let mut code = String::new();
523            writeln!(code, "impl FromXml for {} {{", rust_name).unwrap();
524
525            if has_unresolved_children || has_unresolved_attrs {
526                // Struct with extra_children/extra_attrs — capture unknown XML
527                writeln!(
528                    code,
529                    "    fn from_xml<R: BufRead>(reader: &mut Reader<R>, start_tag: &BytesStart, is_empty: bool) -> Result<Self, ParseError> {{"
530                )
531                .unwrap();
532                if has_unresolved_attrs {
533                    writeln!(code, "        #[cfg(feature = \"extra-attrs\")]").unwrap();
534                    writeln!(
535                        code,
536                        "        let mut extra_attrs = std::collections::HashMap::new();"
537                    )
538                    .unwrap();
539                    writeln!(code, "        #[cfg(feature = \"extra-attrs\")]").unwrap();
540                    writeln!(
541                        code,
542                        "        for attr in start_tag.attributes().filter_map(|a| a.ok()) {{"
543                    )
544                    .unwrap();
545                    writeln!(
546                        code,
547                        "            let key = String::from_utf8_lossy(attr.key.as_ref()).into_owned();"
548                    )
549                    .unwrap();
550                    writeln!(
551                        code,
552                        "            let val = String::from_utf8_lossy(&attr.value).into_owned();"
553                    )
554                    .unwrap();
555                    writeln!(code, "            extra_attrs.insert(key, val);").unwrap();
556                    writeln!(code, "        }}").unwrap();
557                }
558                if has_unresolved_children {
559                    writeln!(code, "        #[cfg(feature = \"extra-children\")]").unwrap();
560                    writeln!(code, "        let mut extra_children = Vec::new();").unwrap();
561                    writeln!(code, "        #[cfg(feature = \"extra-children\")]").unwrap();
562                    writeln!(code, "        let mut child_idx: usize = 0;").unwrap();
563                }
564                writeln!(code, "        if !is_empty {{").unwrap();
565                writeln!(code, "            let mut buf = Vec::new();").unwrap();
566                writeln!(code, "            loop {{").unwrap();
567                writeln!(
568                    code,
569                    "                match reader.read_event_into(&mut buf)? {{"
570                )
571                .unwrap();
572                if has_unresolved_children {
573                    writeln!(
574                        code,
575                        "                    #[cfg(feature = \"extra-children\")]"
576                    )
577                    .unwrap();
578                    writeln!(code, "                    Event::Start(e) => {{").unwrap();
579                    writeln!(
580                        code,
581                        "                        let elem = RawXmlElement::from_reader(reader, &e)?;"
582                    )
583                    .unwrap();
584                    writeln!(
585                        code,
586                        "                        extra_children.push(PositionedNode::new(child_idx, RawXmlNode::Element(elem)));"
587                    )
588                    .unwrap();
589                    writeln!(code, "                        child_idx += 1;").unwrap();
590                    writeln!(code, "                    }}").unwrap();
591                    writeln!(
592                        code,
593                        "                    #[cfg(not(feature = \"extra-children\"))]"
594                    )
595                    .unwrap();
596                    writeln!(
597                        code,
598                        "                    Event::Start(_) => {{ skip_element(reader)?; }}"
599                    )
600                    .unwrap();
601                    writeln!(
602                        code,
603                        "                    #[cfg(feature = \"extra-children\")]"
604                    )
605                    .unwrap();
606                    writeln!(code, "                    Event::Empty(e) => {{").unwrap();
607                    writeln!(
608                        code,
609                        "                        let elem = RawXmlElement::from_empty(&e);"
610                    )
611                    .unwrap();
612                    writeln!(
613                        code,
614                        "                        extra_children.push(PositionedNode::new(child_idx, RawXmlNode::Element(elem)));"
615                    )
616                    .unwrap();
617                    writeln!(code, "                        child_idx += 1;").unwrap();
618                    writeln!(code, "                    }}").unwrap();
619                    writeln!(
620                        code,
621                        "                    #[cfg(not(feature = \"extra-children\"))]"
622                    )
623                    .unwrap();
624                    writeln!(code, "                    Event::Empty(_) => {{}}").unwrap();
625                } else {
626                    writeln!(
627                        code,
628                        "                    Event::Start(_) => {{ skip_element(reader)?; }}"
629                    )
630                    .unwrap();
631                    writeln!(code, "                    Event::Empty(_) => {{}}").unwrap();
632                }
633                writeln!(code, "                    Event::End(_) => break,").unwrap();
634                writeln!(code, "                    Event::Eof => break,").unwrap();
635                writeln!(code, "                    _ => {{}}").unwrap();
636                writeln!(code, "                }}").unwrap();
637                writeln!(code, "                buf.clear();").unwrap();
638                writeln!(code, "            }}").unwrap();
639                writeln!(code, "        }}").unwrap();
640                writeln!(code, "        Ok(Self {{").unwrap();
641                if has_unresolved_attrs {
642                    writeln!(code, "            #[cfg(feature = \"extra-attrs\")]").unwrap();
643                    writeln!(code, "            extra_attrs,").unwrap();
644                }
645                if has_unresolved_children {
646                    writeln!(code, "            #[cfg(feature = \"extra-children\")]").unwrap();
647                    writeln!(code, "            extra_children,").unwrap();
648                }
649                writeln!(code, "        }})").unwrap();
650            } else {
651                // Truly empty struct - skip all children with depth tracking
652                writeln!(
653                    code,
654                    "    fn from_xml<R: BufRead>(reader: &mut Reader<R>, _start: &BytesStart, is_empty: bool) -> Result<Self, ParseError> {{"
655                )
656                .unwrap();
657                writeln!(code, "        if !is_empty {{").unwrap();
658                writeln!(code, "            let mut buf = Vec::new();").unwrap();
659                writeln!(code, "            let mut depth = 1u32;").unwrap();
660                writeln!(code, "            loop {{").unwrap();
661                writeln!(
662                    code,
663                    "                match reader.read_event_into(&mut buf)? {{"
664                )
665                .unwrap();
666                writeln!(code, "                    Event::Start(_) => depth += 1,").unwrap();
667                writeln!(
668                    code,
669                    "                    Event::End(_) => {{ depth -= 1; if depth == 0 {{ break; }} }}"
670                )
671                .unwrap();
672                writeln!(code, "                    Event::Eof => break,").unwrap();
673                writeln!(code, "                    _ => {{}}").unwrap();
674                writeln!(code, "                }}").unwrap();
675                writeln!(code, "                buf.clear();").unwrap();
676                writeln!(code, "            }}").unwrap();
677                writeln!(code, "        }}").unwrap();
678                writeln!(code, "        Ok(Self {{}})").unwrap();
679            }
680
681            writeln!(code, "    }}").unwrap();
682            writeln!(code, "}}").unwrap();
683            return Some(code);
684        }
685
686        let mut code = String::new();
687        writeln!(code, "impl FromXml for {} {{", rust_name).unwrap();
688        writeln!(
689            code,
690            "    fn from_xml<R: BufRead>(reader: &mut Reader<R>, start_tag: &BytesStart, is_empty: bool) -> Result<Self, ParseError> {{"
691        )
692        .unwrap();
693
694        // Declare field variables with f_ prefix to avoid shadowing function parameters
695        for field in &fields {
696            // Strip r# from raw identifiers and leading underscores before prefixing
697            let base_name = field.name.strip_prefix("r#").unwrap_or(&field.name);
698            let base_name = base_name.trim_start_matches('_');
699            let var_name = format!("f_{}", base_name);
700
701            // Add cfg attribute for feature-gated fields
702            if let Some(ref feature) = self.get_field_feature(&rust_name, &field.xml_name) {
703                write!(code, "        #[cfg(feature = \"{}\")] ", feature).unwrap();
704            } else {
705                write!(code, "        ").unwrap();
706            }
707
708            if field.is_vec {
709                writeln!(code, "let mut {} = Vec::new();", var_name).unwrap();
710            } else if field.is_optional {
711                writeln!(code, "let mut {} = None;", var_name).unwrap();
712            } else {
713                // Required non-Vec field - may need boxing for recursive types
714                let (rust_type, needs_box) = self.pattern_to_rust_type(&field.pattern, false);
715                let full_type = if needs_box {
716                    format!("Box<{}>", rust_type)
717                } else {
718                    rust_type
719                };
720                writeln!(code, "let mut {}: Option<{}> = None;", var_name, full_type).unwrap();
721            }
722        }
723
724        // Parse attributes
725        let attr_fields: Vec<_> = fields.iter().filter(|f| f.is_attribute).collect();
726        let has_attrs = !attr_fields.is_empty();
727        let elem_fields: Vec<_> = fields
728            .iter()
729            .filter(|f| !f.is_attribute && !f.is_text_content)
730            .collect();
731        let text_fields: Vec<_> = fields.iter().filter(|f| f.is_text_content).collect();
732        let has_children = !elem_fields.is_empty();
733        let has_parsing_loop = has_children || !text_fields.is_empty();
734        if has_attrs {
735            // Declare extra_attrs for capturing unknown attributes (feature-gated)
736            writeln!(code, "        #[cfg(feature = \"extra-attrs\")]").unwrap();
737            writeln!(
738                code,
739                "        let mut extra_attrs = std::collections::HashMap::new();"
740            )
741            .unwrap();
742        }
743        if has_parsing_loop {
744            // Declare extra_children for capturing unknown child elements (feature-gated)
745            writeln!(code, "        #[cfg(feature = \"extra-children\")]").unwrap();
746            writeln!(code, "        let mut extra_children = Vec::new();").unwrap();
747            writeln!(code, "        #[cfg(feature = \"extra-children\")]").unwrap();
748            writeln!(code, "        let mut child_idx: usize = 0;").unwrap();
749        }
750        if has_attrs || has_parsing_loop {
751            writeln!(code).unwrap();
752        }
753        if has_attrs {
754            writeln!(code, "        // Parse attributes").unwrap();
755            writeln!(
756                code,
757                "        for attr in start_tag.attributes().filter_map(|a| a.ok()) {{"
758            )
759            .unwrap();
760            writeln!(
761                code,
762                "            let val = String::from_utf8_lossy(&attr.value);"
763            )
764            .unwrap();
765            writeln!(code, "            match attr.key.local_name().as_ref() {{").unwrap();
766            for field in &attr_fields {
767                let base_name = field.name.strip_prefix("r#").unwrap_or(&field.name);
768                let base_name = base_name.trim_start_matches('_');
769                let var_name = format!("f_{}", base_name);
770                let parse_expr = self.gen_attr_parse_expr(&field.pattern);
771
772                // Add cfg attribute for feature-gated fields
773                if let Some(ref feature) = self.get_field_feature(&rust_name, &field.xml_name) {
774                    writeln!(code, "                #[cfg(feature = \"{}\")]", feature).unwrap();
775                }
776                writeln!(code, "                b\"{}\" => {{", field.xml_name).unwrap();
777                writeln!(code, "                    {} = {};", var_name, parse_expr).unwrap();
778                writeln!(code, "                }}").unwrap();
779            }
780            // Capture unknown attributes for roundtrip fidelity (feature-gated)
781            writeln!(code, "                #[cfg(feature = \"extra-attrs\")]").unwrap();
782            writeln!(code, "                unknown => {{").unwrap();
783            writeln!(
784                code,
785                "                    let key = String::from_utf8_lossy(attr.key.as_ref()).into_owned();"
786            )
787            .unwrap();
788            writeln!(
789                code,
790                "                    extra_attrs.insert(key, val.into_owned());"
791            )
792            .unwrap();
793            writeln!(code, "                }}").unwrap();
794            writeln!(
795                code,
796                "                #[cfg(not(feature = \"extra-attrs\"))]"
797            )
798            .unwrap();
799            writeln!(code, "                _ => {{}}").unwrap();
800            writeln!(code, "            }}").unwrap();
801            writeln!(code, "        }}").unwrap();
802        }
803
804        // Parse child elements and text content (only if not empty element)
805        if has_parsing_loop {
806            writeln!(code).unwrap();
807            writeln!(code, "        // Parse child elements").unwrap();
808            writeln!(code, "        if !is_empty {{").unwrap();
809            writeln!(code, "            let mut buf = Vec::new();").unwrap();
810            writeln!(code, "            loop {{").unwrap();
811            writeln!(
812                code,
813                "                match reader.read_event_into(&mut buf)? {{"
814            )
815            .unwrap();
816            writeln!(code, "                    Event::Start(e) => {{").unwrap();
817            writeln!(
818                code,
819                "                        match e.local_name().as_ref() {{"
820            )
821            .unwrap();
822
823            // Track matched element names to avoid duplicate match arms
824            let mut matched_names = std::collections::HashSet::new();
825            for field in &elem_fields {
826                let base_name = field.name.strip_prefix("r#").unwrap_or(&field.name);
827                let base_name = base_name.trim_start_matches('_');
828                let var_name = format!("f_{}", base_name);
829                let parse_expr = self.gen_element_parse_code(field, false);
830
831                // Add cfg attribute for feature-gated fields
832                if let Some(ref feature) = self.get_field_feature(&rust_name, &field.xml_name) {
833                    writeln!(
834                        code,
835                        "                            #[cfg(feature = \"{}\")]",
836                        feature
837                    )
838                    .unwrap();
839                }
840                if self.is_eg_content_field(field) {
841                    // EG content field: match all variant element names (dedup across fields)
842                    let mut variant_names = Vec::new();
843                    let mut visited = std::collections::HashSet::new();
844                    if let Some(def_pattern) = self.definitions.get(field.xml_name.as_str()) {
845                        self.collect_element_variant_names(
846                            def_pattern,
847                            &mut variant_names,
848                            &mut visited,
849                        );
850                    }
851                    variant_names.retain(|n| matched_names.insert(n.clone()));
852                    if !variant_names.is_empty() {
853                        let arms: Vec<_> = variant_names
854                            .iter()
855                            .map(|n| format!("b\"{}\"", n))
856                            .collect();
857                        writeln!(
858                            code,
859                            "                            {} => {{",
860                            arms.join(" | ")
861                        )
862                        .unwrap();
863                    } else {
864                        // All variants already matched by earlier field — skip
865                        continue;
866                    }
867                } else {
868                    matched_names.insert(field.xml_name.clone());
869                    writeln!(
870                        code,
871                        "                            b\"{}\" => {{",
872                        field.xml_name
873                    )
874                    .unwrap();
875                }
876                if field.is_vec {
877                    writeln!(
878                        code,
879                        "                                {}.push({});",
880                        var_name, parse_expr
881                    )
882                    .unwrap();
883                } else {
884                    writeln!(
885                        code,
886                        "                                {} = Some({});",
887                        var_name, parse_expr
888                    )
889                    .unwrap();
890                }
891                writeln!(
892                    code,
893                    "                                #[cfg(feature = \"extra-children\")]"
894                )
895                .unwrap();
896                writeln!(
897                    code,
898                    "                                {{ child_idx += 1; }}"
899                )
900                .unwrap();
901                writeln!(code, "                            }}").unwrap();
902            }
903
904            // Capture or skip unknown elements (feature-gated)
905            writeln!(
906                code,
907                "                            #[cfg(feature = \"extra-children\")]"
908            )
909            .unwrap();
910            writeln!(code, "                            _ => {{").unwrap();
911            writeln!(
912                code,
913                "                                // Capture unknown element for roundtrip"
914            )
915            .unwrap();
916            writeln!(code, "                                let elem = RawXmlElement::from_reader(reader, &e)?;").unwrap();
917            writeln!(
918                code,
919                "                                extra_children.push(PositionedNode::new(child_idx, RawXmlNode::Element(elem)));"
920            )
921            .unwrap();
922            writeln!(code, "                                child_idx += 1;").unwrap();
923            writeln!(code, "                            }}").unwrap();
924            writeln!(
925                code,
926                "                            #[cfg(not(feature = \"extra-children\"))]"
927            )
928            .unwrap();
929            writeln!(code, "                            _ => {{").unwrap();
930            writeln!(
931                code,
932                "                                // Skip unknown element"
933            )
934            .unwrap();
935            writeln!(
936                code,
937                "                                skip_element(reader)?;"
938            )
939            .unwrap();
940            writeln!(code, "                            }}").unwrap();
941            writeln!(code, "                        }}").unwrap();
942            writeln!(code, "                    }}").unwrap();
943            writeln!(code, "                    Event::Empty(e) => {{").unwrap();
944            writeln!(
945                code,
946                "                        match e.local_name().as_ref() {{"
947            )
948            .unwrap();
949
950            // Track matched element names to avoid duplicate match arms
951            let mut matched_names_empty = std::collections::HashSet::new();
952            for field in &elem_fields {
953                let base_name = field.name.strip_prefix("r#").unwrap_or(&field.name);
954                let base_name = base_name.trim_start_matches('_');
955                let var_name = format!("f_{}", base_name);
956                let parse_expr = self.gen_element_parse_code(field, true);
957
958                // Add cfg attribute for feature-gated fields
959                if let Some(ref feature) = self.get_field_feature(&rust_name, &field.xml_name) {
960                    writeln!(
961                        code,
962                        "                            #[cfg(feature = \"{}\")]",
963                        feature
964                    )
965                    .unwrap();
966                }
967                if self.is_eg_content_field(field) {
968                    // EG content field: match all variant element names (dedup across fields)
969                    let mut variant_names = Vec::new();
970                    let mut visited = std::collections::HashSet::new();
971                    if let Some(def_pattern) = self.definitions.get(field.xml_name.as_str()) {
972                        self.collect_element_variant_names(
973                            def_pattern,
974                            &mut variant_names,
975                            &mut visited,
976                        );
977                    }
978                    variant_names.retain(|n| matched_names_empty.insert(n.clone()));
979                    if !variant_names.is_empty() {
980                        let arms: Vec<_> = variant_names
981                            .iter()
982                            .map(|n| format!("b\"{}\"", n))
983                            .collect();
984                        writeln!(
985                            code,
986                            "                            {} => {{",
987                            arms.join(" | ")
988                        )
989                        .unwrap();
990                    } else {
991                        // All variants already matched by earlier field — skip
992                        continue;
993                    }
994                } else {
995                    matched_names_empty.insert(field.xml_name.clone());
996                    writeln!(
997                        code,
998                        "                            b\"{}\" => {{",
999                        field.xml_name
1000                    )
1001                    .unwrap();
1002                }
1003                if field.is_vec {
1004                    writeln!(
1005                        code,
1006                        "                                {}.push({});",
1007                        var_name, parse_expr
1008                    )
1009                    .unwrap();
1010                } else {
1011                    writeln!(
1012                        code,
1013                        "                                {} = Some({});",
1014                        var_name, parse_expr
1015                    )
1016                    .unwrap();
1017                }
1018                writeln!(
1019                    code,
1020                    "                                #[cfg(feature = \"extra-children\")]"
1021                )
1022                .unwrap();
1023                writeln!(
1024                    code,
1025                    "                                {{ child_idx += 1; }}"
1026                )
1027                .unwrap();
1028                writeln!(code, "                            }}").unwrap();
1029            }
1030
1031            // Capture or skip unknown empty elements (feature-gated)
1032            writeln!(
1033                code,
1034                "                            #[cfg(feature = \"extra-children\")]"
1035            )
1036            .unwrap();
1037            writeln!(code, "                            _ => {{").unwrap();
1038            writeln!(
1039                code,
1040                "                                // Capture unknown empty element for roundtrip"
1041            )
1042            .unwrap();
1043            writeln!(
1044                code,
1045                "                                let elem = RawXmlElement::from_empty(&e);"
1046            )
1047            .unwrap();
1048            writeln!(
1049                code,
1050                "                                extra_children.push(PositionedNode::new(child_idx, RawXmlNode::Element(elem)));"
1051            )
1052            .unwrap();
1053            writeln!(code, "                                child_idx += 1;").unwrap();
1054            writeln!(code, "                            }}").unwrap();
1055            writeln!(
1056                code,
1057                "                            #[cfg(not(feature = \"extra-children\"))]"
1058            )
1059            .unwrap();
1060            writeln!(code, "                            _ => {{}}").unwrap();
1061            writeln!(code, "                        }}").unwrap();
1062            writeln!(code, "                    }}").unwrap();
1063
1064            // Handle text content if any text fields
1065            if !text_fields.is_empty() {
1066                writeln!(code, "                    Event::Text(e) => {{").unwrap();
1067                for field in &text_fields {
1068                    let base_name = field.name.strip_prefix("r#").unwrap_or(&field.name);
1069                    let base_name = base_name.trim_start_matches('_');
1070                    let var_name = format!("f_{}", base_name);
1071                    writeln!(
1072                        code,
1073                        "                        {} = Some(e.decode().unwrap_or_default().into_owned());",
1074                        var_name
1075                    ).unwrap();
1076                }
1077                writeln!(code, "                    }}").unwrap();
1078            }
1079
1080            writeln!(code, "                    Event::End(_) => break,").unwrap();
1081            writeln!(code, "                    Event::Eof => break,").unwrap();
1082            writeln!(code, "                    _ => {{}}").unwrap();
1083            writeln!(code, "                }}").unwrap();
1084            writeln!(code, "                buf.clear();").unwrap();
1085            writeln!(code, "            }}").unwrap();
1086            writeln!(code, "        }}").unwrap();
1087        } else {
1088            // No child elements, but still need to read to end tag if not empty
1089            writeln!(code).unwrap();
1090            writeln!(code, "        if !is_empty {{").unwrap();
1091            writeln!(code, "            let mut buf = Vec::new();").unwrap();
1092            writeln!(code, "            loop {{").unwrap();
1093            writeln!(
1094                code,
1095                "                match reader.read_event_into(&mut buf)? {{"
1096            )
1097            .unwrap();
1098            writeln!(code, "                    Event::End(_) => break,").unwrap();
1099            writeln!(code, "                    Event::Eof => break,").unwrap();
1100            writeln!(code, "                    _ => {{}}").unwrap();
1101            writeln!(code, "                }}").unwrap();
1102            writeln!(code, "                buf.clear();").unwrap();
1103            writeln!(code, "            }}").unwrap();
1104            writeln!(code, "        }}").unwrap();
1105        }
1106
1107        // Build result struct - use original field names
1108        writeln!(code).unwrap();
1109        writeln!(code, "        Ok(Self {{").unwrap();
1110        for field in &fields {
1111            let base_name = field.name.strip_prefix("r#").unwrap_or(&field.name);
1112            let base_name = base_name.trim_start_matches('_');
1113            let var_name = format!("f_{}", base_name);
1114
1115            // Add cfg attribute for feature-gated fields
1116            if let Some(ref feature) = self.get_field_feature(&rust_name, &field.xml_name) {
1117                writeln!(code, "            #[cfg(feature = \"{}\")]", feature).unwrap();
1118            }
1119
1120            // EG content fields that are required in schema are still Option<Box<...>>
1121            // in Rust for Default compatibility (see codegen.rs eg_needs_option).
1122            let eg_needs_option =
1123                self.is_eg_content_field(field) && !field.is_optional && !field.is_vec;
1124
1125            if field.is_optional || field.is_vec || eg_needs_option {
1126                writeln!(code, "            {}: {},", field.name, var_name).unwrap();
1127            } else {
1128                // Required field - unwrap with error
1129                writeln!(
1130                    code,
1131                    "            {}: {}.ok_or_else(|| ParseError::MissingAttribute(\"{}\".to_string()))?,",
1132                    field.name, var_name, field.xml_name
1133                )
1134                .unwrap();
1135            }
1136        }
1137        // Add extra_attrs if this struct has attributes (feature-gated)
1138        if has_attrs {
1139            writeln!(code, "            #[cfg(feature = \"extra-attrs\")]").unwrap();
1140            writeln!(code, "            extra_attrs,").unwrap();
1141        }
1142        // Add extra_children if this struct has a parsing loop (feature-gated)
1143        if has_parsing_loop {
1144            writeln!(code, "            #[cfg(feature = \"extra-children\")]").unwrap();
1145            writeln!(code, "            extra_children,").unwrap();
1146        }
1147        writeln!(code, "        }})").unwrap();
1148        writeln!(code, "    }}").unwrap();
1149        writeln!(code, "}}").unwrap();
1150
1151        Some(code)
1152    }
1153
1154    fn gen_attr_parse_expr(&self, pattern: &Pattern) -> String {
1155        match pattern {
1156            Pattern::Datatype { library, name, .. } if library == "xsd" => match name.as_str() {
1157                "boolean" => "Some(val == \"true\" || val == \"1\")".to_string(),
1158                "integer" | "int" | "long" | "short" | "byte" => "val.parse().ok()".to_string(),
1159                "unsignedInt" | "unsignedLong" | "unsignedShort" | "unsignedByte" => {
1160                    "val.parse().ok()".to_string()
1161                }
1162                "double" | "float" | "decimal" => "val.parse().ok()".to_string(),
1163                "hexBinary" => "decode_hex(&val)".to_string(),
1164                "base64Binary" => "decode_base64(&val)".to_string(),
1165                _ => "Some(val.into_owned())".to_string(),
1166            },
1167            Pattern::Ref(name) => {
1168                // First recurse to find what this ref resolves to
1169                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
1170                    return self.gen_attr_parse_expr(def_pattern);
1171                }
1172                // Unknown ref - if it looks like an enum (ST_), use FromStr, otherwise string
1173                if name.contains("_ST_") {
1174                    "val.parse().ok()".to_string()
1175                } else {
1176                    "Some(val.into_owned())".to_string()
1177                }
1178            }
1179            // String choice enums
1180            Pattern::Choice(variants)
1181                if variants
1182                    .iter()
1183                    .all(|v| matches!(v, Pattern::StringLiteral(_))) =>
1184            {
1185                "val.parse().ok()".to_string()
1186            }
1187            _ => "Some(val.into_owned())".to_string(),
1188        }
1189    }
1190
1191    fn extract_fields(&self, pattern: &Pattern) -> Vec<Field> {
1192        let mut fields = Vec::new();
1193        self.collect_fields(pattern, &mut fields, false);
1194        let mut seen = std::collections::HashSet::new();
1195        fields.retain(|f| seen.insert(f.name.clone()));
1196        fields
1197    }
1198
1199    fn collect_fields(&self, pattern: &Pattern, fields: &mut Vec<Field>, is_optional: bool) {
1200        match pattern {
1201            Pattern::Attribute { name, pattern } => {
1202                fields.push(Field {
1203                    name: self.qname_to_field_name(name),
1204                    xml_name: name.local.clone(),
1205                    xml_prefix: name.prefix.clone(),
1206                    pattern: pattern.as_ref().clone(),
1207                    is_optional,
1208                    is_attribute: true,
1209                    is_vec: false,
1210                    is_text_content: false,
1211                });
1212            }
1213            Pattern::Element { name, pattern } => {
1214                // Skip wildcard elements (element * { ... }) — handled by extra_children
1215                if name.local == "_any" {
1216                    return;
1217                }
1218                fields.push(Field {
1219                    name: self.qname_to_field_name(name),
1220                    xml_name: name.local.clone(),
1221                    xml_prefix: name.prefix.clone(),
1222                    pattern: pattern.as_ref().clone(),
1223                    is_optional,
1224                    is_attribute: false,
1225                    is_vec: false,
1226                    is_text_content: false,
1227                });
1228            }
1229            Pattern::Sequence(items) | Pattern::Interleave(items) => {
1230                for item in items {
1231                    self.collect_fields(item, fields, is_optional);
1232                }
1233            }
1234            Pattern::Optional(inner) => {
1235                self.collect_fields(inner, fields, true);
1236            }
1237            Pattern::ZeroOrMore(inner) | Pattern::OneOrMore(inner) => match inner.as_ref() {
1238                Pattern::Element { name, pattern } if name.local != "_any" => {
1239                    fields.push(Field {
1240                        name: self.qname_to_field_name(name),
1241                        xml_name: name.local.clone(),
1242                        xml_prefix: name.prefix.clone(),
1243                        pattern: pattern.as_ref().clone(),
1244                        is_optional: false,
1245                        is_attribute: false,
1246                        is_vec: true,
1247                        is_text_content: false,
1248                    });
1249                }
1250                Pattern::Ref(name) if name.contains("_EG_") => {
1251                    if let Some(def_pattern) = self.definitions.get(name.as_str()) {
1252                        if self.is_element_choice(def_pattern) {
1253                            // Element choice → Vec<Box<EGType>> field
1254                            fields.push(Field {
1255                                name: self.eg_ref_to_field_name(name),
1256                                xml_name: name.clone(),
1257                                xml_prefix: None,
1258                                pattern: Pattern::Ref(name.clone()),
1259                                is_optional: false,
1260                                is_attribute: false,
1261                                is_vec: true,
1262                                is_text_content: false,
1263                            });
1264                        } else {
1265                            // Struct-like → inline fields
1266                            self.collect_fields(def_pattern, fields, true);
1267                        }
1268                    }
1269                }
1270                Pattern::Choice(alternatives) => {
1271                    // ZeroOrMore(Choice([elements])) - each element can appear multiple times
1272                    for alt in alternatives {
1273                        self.collect_fields_as_vec(alt, fields);
1274                    }
1275                }
1276                Pattern::Ref(_) => {
1277                    self.collect_fields(inner, fields, false);
1278                }
1279                Pattern::Group(group_inner) => {
1280                    // Unwrap group and handle inner pattern
1281                    // For ZeroOrMore(Group(Choice([...]))) - treat each alternative as Vec
1282                    if let Pattern::Choice(alternatives) = group_inner.as_ref() {
1283                        for alt in alternatives {
1284                            self.collect_fields_as_vec(alt, fields);
1285                        }
1286                    } else {
1287                        self.collect_fields(group_inner, fields, false);
1288                    }
1289                }
1290                _ => {}
1291            },
1292            Pattern::Group(inner) => {
1293                self.collect_fields(inner, fields, is_optional);
1294            }
1295            Pattern::Ref(name) => {
1296                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
1297                    if name.contains("_EG_") {
1298                        if self.is_element_choice(def_pattern) {
1299                            // Element choice → content field (Optional or Vec depending on context)
1300                            fields.push(Field {
1301                                name: self.eg_ref_to_field_name(name),
1302                                xml_name: name.clone(),
1303                                xml_prefix: None,
1304                                pattern: Pattern::Ref(name.clone()),
1305                                is_optional,
1306                                is_attribute: false,
1307                                is_vec: false,
1308                                is_text_content: false,
1309                            });
1310                        } else {
1311                            // Struct-like → inline fields
1312                            self.collect_fields(def_pattern, fields, is_optional);
1313                        }
1314                    } else if name.contains("_AG_") {
1315                        // Attribute group → inline attribute fields
1316                        self.collect_fields(def_pattern, fields, is_optional);
1317                    } else if self.is_string_type(def_pattern) {
1318                        // This is text content - always optional since elements
1319                        // can be self-closing (e.g. shared formula refs: <f t="shared" si="0"/>)
1320                        fields.push(Field {
1321                            name: "text".to_string(),
1322                            xml_name: "$text".to_string(),
1323                            xml_prefix: None,
1324                            pattern: Pattern::Datatype {
1325                                library: "xsd".to_string(),
1326                                name: "string".to_string(),
1327                                params: vec![],
1328                            },
1329                            is_optional: true,
1330                            is_attribute: false,
1331                            is_vec: false,
1332                            is_text_content: true,
1333                        });
1334                    } else {
1335                        // CT_* mixin or base type — inline its fields
1336                        self.collect_fields(def_pattern, fields, is_optional);
1337                    }
1338                }
1339            }
1340            Pattern::Choice(alternatives) => {
1341                // Flatten choice into optional fields.
1342                // In a choice, each alternative might not be selected, so all become optional.
1343                for alt in alternatives {
1344                    self.collect_fields(alt, fields, true);
1345                }
1346            }
1347            _ => {}
1348        }
1349    }
1350
1351    /// Collect fields as Vec (for elements inside ZeroOrMore(Choice([...])))
1352    /// Only non-optional elements become Vec; optional elements stay as Option.
1353    fn collect_fields_as_vec(&self, pattern: &Pattern, fields: &mut Vec<Field>) {
1354        match pattern {
1355            Pattern::Element {
1356                name,
1357                pattern: inner_pattern,
1358            } if name.local != "_any" => {
1359                fields.push(Field {
1360                    name: self.qname_to_field_name(name),
1361                    xml_name: name.local.clone(),
1362                    xml_prefix: name.prefix.clone(),
1363                    pattern: inner_pattern.as_ref().clone(),
1364                    is_optional: false,
1365                    is_attribute: false,
1366                    is_vec: true,
1367                    is_text_content: false,
1368                });
1369            }
1370            Pattern::Optional(inner) => {
1371                // Optional inside a repeating choice means element can appear 0-1 times,
1372                // NOT multiple times. Delegate to collect_fields with is_optional=true.
1373                self.collect_fields(inner, fields, true);
1374            }
1375            Pattern::Group(inner) => {
1376                self.collect_fields_as_vec(inner, fields);
1377            }
1378            Pattern::Ref(name) => {
1379                // Ref inside a repeating choice - create Vec<RefType> field
1380                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
1381                    if name.contains("_EG_") && self.is_element_choice(def_pattern) {
1382                        // EG_* element group → Vec<EGType>
1383                        fields.push(Field {
1384                            name: self.eg_ref_to_field_name(name),
1385                            xml_name: name.clone(),
1386                            xml_prefix: None,
1387                            pattern: Pattern::Ref(name.clone()),
1388                            is_optional: false,
1389                            is_attribute: false,
1390                            is_vec: true,
1391                            is_text_content: false,
1392                        });
1393                    } else if !name.contains("_AG_") && !name.contains("_CT_") {
1394                        // Other refs that wrap elements
1395                        self.collect_fields_as_vec(def_pattern, fields);
1396                    }
1397                }
1398            }
1399            _ => {}
1400        }
1401    }
1402
1403    /// Check if a pattern resolves to a string type (for text content detection).
1404    fn is_string_type(&self, pattern: &Pattern) -> bool {
1405        match pattern {
1406            Pattern::Datatype { library, name, .. } => {
1407                library == "xsd" && (name == "string" || name == "token" || name == "NCName")
1408            }
1409            Pattern::Ref(name) => {
1410                // Check if the referenced type is a string type
1411                self.definitions
1412                    .get(name.as_str())
1413                    .is_some_and(|p| self.is_string_type(p))
1414            }
1415            _ => false,
1416        }
1417    }
1418
1419    fn to_rust_type_name(&self, name: &str) -> String {
1420        let spec_name = strip_namespace_prefix(name);
1421        if let Some(mappings) = &self.config.name_mappings
1422            && let Some(mapped) = mappings.resolve_type(&self.config.module_name, spec_name)
1423        {
1424            return mapped.to_string();
1425        }
1426        to_pascal_case(spec_name)
1427    }
1428
1429    /// Try to resolve a schema reference name to a cross-crate Rust type.
1430    /// Returns Some(full_path) if the name matches a configured prefix.
1431    fn resolve_cross_crate_type(&self, name: &str) -> Option<String> {
1432        for (prefix, (crate_path, module_name)) in &self.config.cross_crate_type_prefix {
1433            if name.starts_with(prefix) {
1434                // Convert schema name (a_CT_Color) to Rust type name using the cross-crate module's mappings
1435                let spec_name = strip_namespace_prefix(name);
1436                let rust_type_name = if let Some(mappings) = &self.config.name_mappings
1437                    && let Some(mapped) = mappings.resolve_type(module_name, spec_name)
1438                {
1439                    mapped.to_string()
1440                } else {
1441                    to_pascal_case(spec_name)
1442                };
1443                return Some(format!("{}{}", crate_path, rust_type_name));
1444            }
1445        }
1446        None
1447    }
1448
1449    fn to_rust_variant_name(&self, name: &str) -> String {
1450        if name.is_empty() {
1451            return "Empty".to_string();
1452        }
1453        if let Some(mappings) = &self.config.name_mappings
1454            && let Some(mapped) = mappings.resolve_variant(&self.config.module_name, name)
1455        {
1456            return mapped.to_string();
1457        }
1458        let name = to_pascal_case(name);
1459        if name.chars().next().is_some_and(|c| c.is_ascii_digit()) {
1460            format!("_{}", name)
1461        } else {
1462            name
1463        }
1464    }
1465
1466    fn qname_to_field_name(&self, qname: &QName) -> String {
1467        if let Some(mappings) = &self.config.name_mappings
1468            && let Some(mapped) = mappings.resolve_field(&self.config.module_name, &qname.local)
1469        {
1470            return mapped.to_string();
1471        }
1472        to_snake_case(&qname.local)
1473    }
1474
1475    /// Get the feature name for a field if it requires feature gating.
1476    /// Returns None if the field is "core" (always included) or unmapped.
1477    fn get_field_feature(&self, struct_name: &str, xml_field_name: &str) -> Option<String> {
1478        self.config
1479            .feature_mappings
1480            .as_ref()
1481            .and_then(|fm| {
1482                fm.primary_feature(&self.config.module_name, struct_name, xml_field_name)
1483            })
1484            .map(|feature| format!("{}-{}", self.config.module_name, feature))
1485    }
1486
1487    /// Check if a definition is an element-wrapper type alias (Element { pattern: Ref(...) }).
1488    /// These generate `pub type Foo = Box<T>;` and should not be double-boxed when used.
1489    fn is_element_wrapper_type_alias(&self, name: &str) -> bool {
1490        if let Some(def_pattern) = self.definitions.get(name) {
1491            matches!(def_pattern, Pattern::Element { pattern, .. } if matches!(pattern.as_ref(), Pattern::Ref(_)))
1492        } else {
1493            false
1494        }
1495    }
1496
1497    /// Convert a pattern to Rust type and boxing requirement.
1498    /// - `is_vec`: if true, don't indicate boxing needed (Vec provides heap indirection)
1499    fn pattern_to_rust_type(&self, pattern: &Pattern, is_vec: bool) -> (String, bool) {
1500        match pattern {
1501            Pattern::Ref(name) => {
1502                if self.definitions.contains_key(name.as_str()) {
1503                    let type_name = self.to_rust_type_name(name);
1504                    // Box CT_* and EG_* types, but not in Vec context (Vec provides heap indirection)
1505                    // Also don't box element-wrapper type aliases - they're already Box<T>
1506                    let is_complex = name.contains("_CT_") || name.contains("_EG_");
1507                    let is_already_boxed = self.is_element_wrapper_type_alias(name);
1508                    let needs_box = is_complex && !is_vec && !is_already_boxed;
1509                    (type_name, needs_box)
1510                } else if let Some(cross_crate) = self.resolve_cross_crate_type(name) {
1511                    // Cross-crate type - box CT_* or EG_*, but not in Vec context
1512                    let is_complex = name.contains("_CT_") || name.contains("_EG_");
1513                    let needs_box = is_complex && !is_vec;
1514                    (cross_crate, needs_box)
1515                } else {
1516                    ("String".to_string(), false)
1517                }
1518            }
1519            Pattern::Datatype { library, name, .. } => {
1520                (xsd_to_rust(library, name).to_string(), false)
1521            }
1522            Pattern::Empty => ("()".to_string(), false),
1523            Pattern::StringLiteral(_) => ("String".to_string(), false),
1524            Pattern::Choice(_) => ("String".to_string(), false),
1525            _ => ("String".to_string(), false),
1526        }
1527    }
1528
1529    /// Generate code to parse a child element field.
1530    /// Returns the expression that produces the parsed value.
1531    fn gen_element_parse_code(&self, field: &Field, is_empty_element: bool) -> String {
1532        // Pass is_vec to avoid boxing in Vec contexts
1533        let (rust_type, needs_box) = self.pattern_to_rust_type(&field.pattern, field.is_vec);
1534        let strategy = self.get_parse_strategy(&field.pattern);
1535
1536        let parse_expr = match strategy {
1537            ParseStrategy::FromXml => {
1538                // For type aliases, we need to find the actual type that has FromXml impl
1539                // and check if the type alias already includes a Box
1540                let (actual_type, type_alias_has_box) =
1541                    self.resolve_from_xml_type_with_box(&field.pattern);
1542                let final_type = actual_type.unwrap_or(rust_type.clone());
1543
1544                let mut expr = format!(
1545                    "{}::from_xml(reader, &e, {})?",
1546                    final_type, is_empty_element
1547                );
1548
1549                // If the type alias is like `type X = Box<Y>`, we need a Box for that
1550                if type_alias_has_box {
1551                    expr = format!("Box::new({})", expr);
1552                }
1553
1554                // If the field type is `Box<X>`, we need another Box for the field
1555                if needs_box {
1556                    expr = format!("Box::new({})", expr);
1557                }
1558
1559                return expr;
1560            }
1561            ParseStrategy::TextFromStr => {
1562                if is_empty_element {
1563                    // Empty element with FromStr - try to parse empty string or use default
1564                    "Default::default()".to_string()
1565                } else {
1566                    "{ let text = read_text_content(reader)?; text.parse().map_err(|_| ParseError::InvalidValue(text))? }".to_string()
1567                }
1568            }
1569            ParseStrategy::TextString => {
1570                if is_empty_element {
1571                    "String::new()".to_string()
1572                } else {
1573                    "read_text_content(reader)?".to_string()
1574                }
1575            }
1576            ParseStrategy::TextHexBinary => {
1577                if is_empty_element {
1578                    "Vec::new()".to_string()
1579                } else {
1580                    "{ let text = read_text_content(reader)?; decode_hex(&text).unwrap_or_default() }".to_string()
1581                }
1582            }
1583            ParseStrategy::TextBase64Binary => {
1584                if is_empty_element {
1585                    "Vec::new()".to_string()
1586                } else {
1587                    "{ let text = read_text_content(reader)?; decode_base64(&text).unwrap_or_default() }".to_string()
1588                }
1589            }
1590        };
1591
1592        // For non-FromXml strategies, handle boxing normally
1593        if strategy != ParseStrategy::FromXml && needs_box {
1594            format!("Box::new({})", parse_expr)
1595        } else {
1596            parse_expr
1597        }
1598    }
1599
1600    /// Resolve a pattern to the actual type that has a FromXml impl.
1601    /// Returns (Option<type_name>, type_alias_already_has_box).
1602    /// For type aliases (definitions that are just Pattern::Element wrapping a ref),
1603    /// returns the underlying type's Rust name and indicates the alias includes a Box.
1604    fn resolve_from_xml_type_with_box(&self, pattern: &Pattern) -> (Option<String>, bool) {
1605        match pattern {
1606            Pattern::Ref(name) => {
1607                // Look up the definition
1608                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
1609                    // Skip inline attribute refs (like r_id) - they don't have FromXml impls
1610                    if self.is_inline_attribute_ref(name, def_pattern) {
1611                        return (None, false);
1612                    }
1613                    // If it's a type alias (Pattern::Element wrapping another ref),
1614                    // the generated type will be `type X = Box<InnerType>` for CT_*/EG_* inner types
1615                    // So we resolve to the inner type and signal boxing status
1616                    if let Pattern::Element { pattern: inner, .. } = def_pattern
1617                        && let Some(inner_type) = self.resolve_from_xml_type_simple(inner)
1618                    {
1619                        // Check if the inner ref is to a CT_* or EG_* type (which gets boxed)
1620                        let type_alias_has_box = if let Pattern::Ref(inner_name) = inner.as_ref() {
1621                            inner_name.contains("_CT_") || inner_name.contains("_EG_")
1622                        } else {
1623                            false
1624                        };
1625                        return (Some(inner_type), type_alias_has_box);
1626                    }
1627                    // CT_* types that are NOT type aliases have their own FromXml impl
1628                    // (generated as structs), even if schema defines them as `CT_Foo = EG_Bar`.
1629                    // Don't resolve through - use the CT type directly.
1630                    if name.contains("_CT_") {
1631                        return (Some(self.to_rust_type_name(name)), false);
1632                    }
1633                    // If it's a simple ref, just a type alias like `type X = Y`
1634                    // Check if the inner ref is external (not in our definitions)
1635                    if let Pattern::Ref(inner_name) = def_pattern
1636                        && !self.definitions.contains_key(inner_name.as_str())
1637                    {
1638                        // External ref - check if it's a cross-crate type
1639                        if let Some(cross_crate) = self.resolve_cross_crate_type(inner_name) {
1640                            return (Some(cross_crate), false);
1641                        }
1642                        // Otherwise don't try to resolve, use original type
1643                        return (None, false);
1644                    } else if let Pattern::Ref(inner_name) = def_pattern {
1645                        return self
1646                            .resolve_from_xml_type_with_box(&Pattern::Ref(inner_name.clone()));
1647                    }
1648                } else if let Some(cross_crate) = self.resolve_cross_crate_type(name) {
1649                    // Not in local definitions but matches a cross-crate prefix
1650                    return (Some(cross_crate), false);
1651                }
1652                // Type in definitions - use its name
1653                (Some(self.to_rust_type_name(name)), false)
1654            }
1655            _ => (None, false),
1656        }
1657    }
1658
1659    /// Simple resolver that just returns the type name without box tracking.
1660    fn resolve_from_xml_type_simple(&self, pattern: &Pattern) -> Option<String> {
1661        match pattern {
1662            Pattern::Ref(name) => {
1663                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
1664                    if let Pattern::Element { pattern: inner, .. } = def_pattern {
1665                        return self.resolve_from_xml_type_simple(inner);
1666                    }
1667                    if let Pattern::Ref(inner_name) = def_pattern {
1668                        // Check if the inner ref is external
1669                        if !self.definitions.contains_key(inner_name.as_str()) {
1670                            // External ref - check if it's a cross-crate type
1671                            if let Some(cross_crate) = self.resolve_cross_crate_type(inner_name) {
1672                                return Some(cross_crate);
1673                            }
1674                            // Otherwise stop resolution
1675                            return None;
1676                        }
1677                        return self
1678                            .resolve_from_xml_type_simple(&Pattern::Ref(inner_name.clone()));
1679                    }
1680                } else if let Some(cross_crate) = self.resolve_cross_crate_type(name) {
1681                    // Not in local definitions but matches a cross-crate prefix
1682                    return Some(cross_crate);
1683                }
1684                Some(self.to_rust_type_name(name))
1685            }
1686            _ => None,
1687        }
1688    }
1689
1690    /// Determine how to parse a field's value from XML.
1691    fn get_parse_strategy(&self, pattern: &Pattern) -> ParseStrategy {
1692        match pattern {
1693            Pattern::Ref(name) => {
1694                // Complex types and element groups always use FromXml
1695                if name.contains("_CT_") || name.contains("_EG_") {
1696                    return ParseStrategy::FromXml;
1697                }
1698
1699                // Check what this ref resolves to
1700                if let Some(def_pattern) = self.definitions.get(name.as_str()) {
1701                    // If it's a ref to another ref (like CT_Drawing = r_id),
1702                    // and that ref is external (not in our definitions), treat as empty struct
1703                    if let Pattern::Ref(inner_name) = def_pattern
1704                        && !self.definitions.contains_key(inner_name.as_str())
1705                    {
1706                        // External ref - check if it's a cross-crate CT/EG type
1707                        if inner_name.contains("_CT_") || inner_name.contains("_EG_") {
1708                            return ParseStrategy::FromXml;
1709                        }
1710                        // External ref (e.g., r_id from another schema)
1711                        // These generate empty structs that need simple FromXml impls
1712                        return ParseStrategy::FromXml;
1713                    }
1714                    return self.get_parse_strategy(def_pattern);
1715                } else if self.resolve_cross_crate_type(name).is_some() {
1716                    // Cross-crate type - use FromXml
1717                    return ParseStrategy::FromXml;
1718                }
1719
1720                // Unknown ref - treat as string
1721                ParseStrategy::TextString
1722            }
1723            Pattern::Datatype { library, name, .. } => {
1724                if library == "xsd" {
1725                    match name.as_str() {
1726                        "string" | "token" | "NCName" | "ID" | "IDREF" | "anyURI" | "dateTime"
1727                        | "date" | "time" => ParseStrategy::TextString,
1728                        "hexBinary" => ParseStrategy::TextHexBinary,
1729                        "base64Binary" => ParseStrategy::TextBase64Binary,
1730                        // Numbers and booleans use FromStr
1731                        _ => ParseStrategy::TextFromStr,
1732                    }
1733                } else {
1734                    ParseStrategy::TextString
1735                }
1736            }
1737            // String enums use FromStr
1738            Pattern::Choice(variants)
1739                if variants
1740                    .iter()
1741                    .all(|v| matches!(v, Pattern::StringLiteral(_))) =>
1742            {
1743                ParseStrategy::TextFromStr
1744            }
1745            Pattern::StringLiteral(_) => ParseStrategy::TextString,
1746            Pattern::Empty => ParseStrategy::TextString,
1747            Pattern::List(_) => ParseStrategy::TextString,
1748            // For complex patterns (Sequence, etc.), assume it needs from_xml
1749            _ => ParseStrategy::FromXml,
1750        }
1751    }
1752}
1753
1754pub(crate) struct Field {
1755    pub name: String,
1756    pub xml_name: String,
1757    pub xml_prefix: Option<String>,
1758    pub pattern: Pattern,
1759    pub is_optional: bool,
1760    pub is_attribute: bool,
1761    pub is_vec: bool,
1762    pub is_text_content: bool,
1763}
1764
1765pub(crate) fn strip_namespace_prefix(name: &str) -> &str {
1766    for kind in ["CT_", "ST_", "EG_"] {
1767        if let Some(pos) = name.find(kind)
1768            && pos > 0
1769        {
1770            return &name[pos..];
1771        }
1772    }
1773    name
1774}
1775
1776pub(crate) fn to_pascal_case(s: &str) -> String {
1777    let mut result = String::new();
1778    let mut capitalize_next = true;
1779    for ch in s.chars() {
1780        if ch == '_' || ch == '-' {
1781            capitalize_next = true;
1782        } else if capitalize_next {
1783            result.extend(ch.to_uppercase());
1784            capitalize_next = false;
1785        } else {
1786            result.push(ch);
1787        }
1788    }
1789    result
1790}
1791
1792pub(crate) fn to_snake_case(s: &str) -> String {
1793    let mut result = String::new();
1794    for (i, ch) in s.chars().enumerate() {
1795        if ch.is_uppercase() && i > 0 {
1796            result.push('_');
1797        }
1798        result.extend(ch.to_lowercase());
1799    }
1800    match result.as_str() {
1801        // Rust keywords
1802        "type" => "r#type".to_string(),
1803        "ref" => "r#ref".to_string(),
1804        "match" => "r#match".to_string(),
1805        "in" => "r#in".to_string(),
1806        "for" => "r#for".to_string(),
1807        "macro" => "r#macro".to_string(),
1808        "if" => "r#if".to_string(),
1809        "else" => "r#else".to_string(),
1810        "loop" => "r#loop".to_string(),
1811        "break" => "r#break".to_string(),
1812        "continue" => "r#continue".to_string(),
1813        "return" => "r#return".to_string(),
1814        "self" => "r#self".to_string(),
1815        "super" => "r#super".to_string(),
1816        "crate" => "r#crate".to_string(),
1817        "mod" => "r#mod".to_string(),
1818        "pub" => "r#pub".to_string(),
1819        "use" => "r#use".to_string(),
1820        "as" => "r#as".to_string(),
1821        "static" => "r#static".to_string(),
1822        "const" => "r#const".to_string(),
1823        "extern" => "r#extern".to_string(),
1824        "fn" => "r#fn".to_string(),
1825        "struct" => "r#struct".to_string(),
1826        "enum" => "r#enum".to_string(),
1827        "trait" => "r#trait".to_string(),
1828        "impl" => "r#impl".to_string(),
1829        "where" => "r#where".to_string(),
1830        "async" => "r#async".to_string(),
1831        "await" => "r#await".to_string(),
1832        "move" => "r#move".to_string(),
1833        "box" => "r#box".to_string(),
1834        "dyn" => "r#dyn".to_string(),
1835        "abstract" => "r#abstract".to_string(),
1836        "become" => "r#become".to_string(),
1837        "do" => "r#do".to_string(),
1838        "final" => "r#final".to_string(),
1839        "override" => "r#override".to_string(),
1840        "priv" => "r#priv".to_string(),
1841        "typeof" => "r#typeof".to_string(),
1842        "unsized" => "r#unsized".to_string(),
1843        "virtual" => "r#virtual".to_string(),
1844        "yield" => "r#yield".to_string(),
1845        "try" => "r#try".to_string(),
1846        _ => result,
1847    }
1848}
1849
1850pub(crate) fn xsd_to_rust(library: &str, name: &str) -> &'static str {
1851    if library == "xsd" {
1852        match name {
1853            "string" => "String",
1854            "integer" => "i64",
1855            "int" => "i32",
1856            "long" => "i64",
1857            "short" => "i16",
1858            "byte" => "i8",
1859            "unsignedInt" => "u32",
1860            "unsignedLong" => "u64",
1861            "unsignedShort" => "u16",
1862            "unsignedByte" => "u8",
1863            "boolean" => "bool",
1864            "double" => "f64",
1865            "float" => "f32",
1866            "decimal" => "f64",
1867            "hexBinary" | "base64Binary" => "Vec<u8>",
1868            _ => "String",
1869        }
1870    } else {
1871        "String"
1872    }
1873}