mx20022_codegen/ir/types.rs
1//! Intermediate representation types.
2//!
3//! The IR sits between the raw XSD AST ([`crate::xsd::Schema`]) and the final
4//! emitted Rust token stream. It is closer to Rust than XSD: XML names have
5//! been mapped to Rust identifiers, cardinality has been resolved into
6//! [`Cardinality`] variants, and built-in XSD types have been mapped to
7//! [`RustType`].
8//!
9//! # Entry point
10//!
11//! See [`TypeGraph`] for the top-level structure produced by
12//! [`crate::ir::lower::lower`].
13
14use indexmap::IndexMap;
15
16/// The complete intermediate representation of a single XSD schema.
17///
18/// # Example
19///
20/// ```no_run
21/// use mx20022_codegen::{xsd, ir};
22///
23/// let schema = xsd::parse_str(r#"<xs:schema
24/// xmlns:xs="http://www.w3.org/2001/XMLSchema"
25/// targetNamespace="urn:example">
26/// <xs:element name="Root" type="RootType"/>
27/// <xs:complexType name="RootType">
28/// <xs:sequence>
29/// <xs:element name="Id" type="xs:string"/>
30/// </xs:sequence>
31/// </xs:complexType>
32/// </xs:schema>"#).unwrap();
33///
34/// let graph = ir::lower::lower(&schema).unwrap();
35/// assert_eq!(graph.root_elements.len(), 1);
36/// ```
37#[derive(Debug, Clone, PartialEq)]
38pub struct TypeGraph {
39 /// The `targetNamespace` from the XSD schema element.
40 pub namespace: String,
41 /// Top-level document element declarations (e.g. `<xs:element name="AppHdr" …/>`).
42 pub root_elements: Vec<RootElement>,
43 /// All type definitions keyed by their XSD/Rust name, in insertion order.
44 pub types: IndexMap<String, TypeDef>,
45}
46
47/// A top-level element declaration referencing a named type.
48#[derive(Debug, Clone, PartialEq)]
49pub struct RootElement {
50 /// The XML element name (e.g. `"AppHdr"`).
51 pub xml_name: String,
52 /// The type name the element uses (e.g. `"BusinessApplicationHeaderV04"`).
53 pub type_name: String,
54}
55
56/// A single type definition in the IR.
57#[derive(Debug, Clone, PartialEq)]
58pub enum TypeDef {
59 /// A struct with named fields — from `xs:complexType` with `xs:sequence`.
60 Struct(StructDef),
61 /// An enum with typed variants — from `xs:complexType` with `xs:choice`.
62 Enum(EnumDef),
63 /// A newtype wrapping a built-in Rust type — from `xs:simpleType` with
64 /// non-enumeration facets (pattern, length, etc.).
65 Newtype(NewtypeDef),
66 /// A string-valued code enum — from `xs:simpleType` whose restriction
67 /// contains only `xs:enumeration` facets.
68 CodeEnum(CodeEnumDef),
69 /// A value with XML attributes — from `xs:complexType` with
70 /// `xs:simpleContent` / `xs:extension`.
71 ValueWithAttr(ValueWithAttrDef),
72 /// An opaque wrapper for `xs:any` content.
73 Opaque(OpaqueDef),
74}
75
76impl TypeDef {
77 /// Return the name of this type definition.
78 pub fn name(&self) -> &str {
79 match self {
80 TypeDef::Struct(d) => &d.name,
81 TypeDef::Enum(d) => &d.name,
82 TypeDef::Newtype(d) => &d.name,
83 TypeDef::CodeEnum(d) => &d.name,
84 TypeDef::ValueWithAttr(d) => &d.name,
85 TypeDef::Opaque(d) => &d.name,
86 }
87 }
88}
89
90// ── Struct ────────────────────────────────────────────────────────────────────
91
92/// A struct generated from an `xs:complexType` with an `xs:sequence`.
93#[derive(Debug, Clone, PartialEq)]
94pub struct StructDef {
95 /// The Rust struct name (identical to the XSD type name; already `PascalCase`).
96 pub name: String,
97 /// Ordered list of struct fields.
98 pub fields: Vec<FieldDef>,
99}
100
101/// A single field inside a [`StructDef`].
102#[derive(Debug, Clone, PartialEq)]
103pub struct FieldDef {
104 /// Original XML element name (e.g. `"BizMsgIdr"`).
105 pub xml_name: String,
106 /// `snake_case` Rust field name (e.g. `"biz_msg_idr"`).
107 pub rust_name: String,
108 /// Resolved type reference.
109 pub type_ref: TypeRef,
110 /// Field cardinality, derived from `minOccurs`/`maxOccurs`.
111 pub cardinality: Cardinality,
112}
113
114/// How many times a field may appear.
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub enum Cardinality {
117 /// `minOccurs=1`, `maxOccurs=1` — plain field.
118 Required,
119 /// `minOccurs=0`, `maxOccurs=1` — `Option<T>`.
120 Optional,
121 /// `maxOccurs="unbounded"` — `Vec<T>`.
122 Vec,
123 /// `maxOccurs=N` where `N > 1` — `Vec<T>` with a documented upper bound.
124 BoundedVec(u32),
125}
126
127// ── Enum ──────────────────────────────────────────────────────────────────────
128
129/// An enum generated from an `xs:complexType` with an `xs:choice`.
130#[derive(Debug, Clone, PartialEq)]
131pub struct EnumDef {
132 /// The Rust enum name.
133 pub name: String,
134 /// Ordered list of enum variants.
135 pub variants: Vec<VariantDef>,
136}
137
138/// A single variant inside an [`EnumDef`].
139#[derive(Debug, Clone, PartialEq)]
140pub struct VariantDef {
141 /// Original XML element name used as the variant label (e.g. `"OrgId"`).
142 pub xml_name: String,
143 /// Rust variant name — kept as `PascalCase` since that is already idiomatic
144 /// for enum variants (e.g. `"OrgId"`).
145 pub rust_name: String,
146 /// The inner type carried by this variant.
147 pub type_ref: TypeRef,
148}
149
150// ── Newtype ───────────────────────────────────────────────────────────────────
151
152/// A newtype wrapping a primitive type with optional validation constraints.
153///
154/// Generated from `xs:simpleType` restrictions that do **not** consist solely
155/// of `xs:enumeration` facets.
156#[derive(Debug, Clone, PartialEq)]
157pub struct NewtypeDef {
158 /// The Rust struct name.
159 pub name: String,
160 /// The wrapped primitive Rust type.
161 pub inner: RustType,
162 /// Zero or more constraints derived from XSD facets.
163 pub constraints: Vec<Constraint>,
164}
165
166// ── CodeEnum ──────────────────────────────────────────────────────────────────
167
168/// A string-code enum generated from `xs:simpleType` with `xs:enumeration`
169/// facets.
170///
171/// # Example
172///
173/// XSD `AddressType2Code` with values `"ADDR"`, `"PBOX"`, … becomes:
174/// ```text
175/// pub enum AddressType2Code { Addr, Pbox, … }
176/// ```
177#[derive(Debug, Clone, PartialEq)]
178pub struct CodeEnumDef {
179 /// The Rust enum name.
180 pub name: String,
181 /// The enumerated code values.
182 pub codes: Vec<CodeValue>,
183}
184
185/// A single code value within a [`CodeEnumDef`].
186#[derive(Debug, Clone, PartialEq)]
187pub struct CodeValue {
188 /// The raw XML string value (e.g. `"ADDR"`).
189 pub xml_value: String,
190 /// The Rust variant name in `PascalCase` (e.g. `"Addr"`).
191 pub rust_name: String,
192}
193
194// ── ValueWithAttr ─────────────────────────────────────────────────────────────
195
196/// A value type that also carries XML attributes.
197///
198/// Generated from `xs:complexType` with `xs:simpleContent` / `xs:extension`.
199#[derive(Debug, Clone, PartialEq)]
200pub struct ValueWithAttrDef {
201 /// The Rust struct name.
202 pub name: String,
203 /// The base value type (the `xs:extension base` resolved to an IR ref).
204 pub value_type: TypeRef,
205 /// XML attributes on this element.
206 pub attributes: Vec<AttrDef>,
207}
208
209/// A single XML attribute inside a [`ValueWithAttrDef`].
210#[derive(Debug, Clone, PartialEq)]
211pub struct AttrDef {
212 /// Original XML attribute name (e.g. `"Ccy"`).
213 pub xml_name: String,
214 /// `snake_case` Rust field name (e.g. `"ccy"`).
215 pub rust_name: String,
216 /// Resolved type reference.
217 pub type_ref: TypeRef,
218 /// Whether `use="required"` was specified on the attribute.
219 pub required: bool,
220}
221
222// ── Opaque ────────────────────────────────────────────────────────────────────
223
224/// An opaque wrapper generated from `xs:complexType` with `xs:any`.
225#[derive(Debug, Clone, PartialEq)]
226pub struct OpaqueDef {
227 /// The Rust struct name.
228 pub name: String,
229 /// The `namespace` attribute of `<xs:any>`, if present.
230 pub namespace: Option<String>,
231}
232
233// ── Shared references and primitives ─────────────────────────────────────────
234
235/// A reference to a type in the IR.
236#[derive(Debug, Clone, PartialEq)]
237pub enum TypeRef {
238 /// Reference to another named type in the [`TypeGraph`].
239 Named(String),
240 /// A built-in primitive Rust type.
241 Builtin(RustType),
242}
243
244/// Primitive Rust types that XSD built-in types map to.
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
246pub enum RustType {
247 /// `xs:string`, `xs:normalizedString`, `xs:token`, etc. → `String`
248 String,
249 /// `xs:boolean` → `bool`
250 Bool,
251 /// `xs:decimal` → stored as `String` for now; can be refined to
252 /// `rust_decimal::Decimal` later.
253 Decimal,
254 /// `xs:date` → stored as `String`.
255 Date,
256 /// `xs:dateTime` → stored as `String`.
257 DateTime,
258}
259
260/// A constraint derived from an XSD facet applied to a [`NewtypeDef`].
261#[derive(Debug, Clone, PartialEq)]
262pub enum Constraint {
263 /// `xs:minLength`
264 MinLength(u64),
265 /// `xs:maxLength`
266 MaxLength(u64),
267 /// `xs:pattern`
268 Pattern(String),
269 /// `xs:minInclusive`
270 MinInclusive(String),
271 /// `xs:maxInclusive`
272 MaxInclusive(String),
273 /// `xs:totalDigits`
274 TotalDigits(u32),
275 /// `xs:fractionDigits`
276 FractionDigits(u32),
277}