xsd_parser/
lib.rs

1#![recursion_limit = "256"]
2#![doc = include_str!(concat!(env!("OUT_DIR"), "/README.md"))]
3
4pub mod code;
5pub mod config;
6pub mod generator;
7pub mod interpreter;
8pub mod optimizer;
9pub mod parser;
10pub mod quick_xml;
11pub mod schema;
12pub mod types;
13
14mod macros;
15mod misc;
16
17/// Type alias for [`generator::Error`].
18pub type GeneratorError = generator::Error;
19
20/// Type alias for [`interpreter::Error`].
21pub type InterpreterError = interpreter::Error;
22
23/// Type alias for [`parser::Error`].
24pub type ParserError<E> = parser::Error<E>;
25
26use std::fs::write;
27
28pub use code::{Module, SubModules};
29pub use config::Config;
30pub use generator::Generator;
31pub use interpreter::Interpreter;
32pub use misc::{AsAny, Error, WithNamespace};
33pub use optimizer::Optimizer;
34pub use parser::Parser;
35
36use macros::{assert_eq, unreachable};
37use proc_macro2::TokenStream;
38use quote::ToTokens;
39use tracing::instrument;
40
41use self::config::{
42    Generate, GeneratorConfig, InterpreterConfig, InterpreterFlags, OptimizerConfig,
43    OptimizerFlags, ParserConfig, ParserFlags, Renderer, Resolver, Schema,
44};
45use self::generator::renderer::{
46    DefaultsRenderer, NamespaceConstantsRenderer, QuickXmlDeserializeRenderer,
47    QuickXmlSerializeRenderer, TypesRenderer, WithNamespaceTraitRenderer,
48};
49use self::misc::TypesPrinter;
50use self::parser::resolver::{FileResolver, ManyResolver};
51use self::schema::Schemas;
52use self::types::{IdentType, Types};
53
54/// Generates rust code from a XML schema using the passed `config`.
55///
56/// This is the most easiest way to use the `xsd-parser` crate. The `generate`
57/// function provides a simple way to generate rust code from XML schemas using
58/// the passed configuration.
59///
60/// If you need more detailed control over the generation process or only a part
61/// of it, use the [`Parser`], [`Interpreter`], [`Optimizer`] or [`Generator`]
62/// directly.
63///
64/// # Errors
65///
66/// Returns a suitable [`Error`] type if the process was not successful.
67#[instrument(err, level = "trace")]
68pub fn generate(config: Config) -> Result<TokenStream, Error> {
69    let module = generate_modules(config)?;
70    let code = module.to_token_stream();
71
72    Ok(code)
73}
74
75/// Generates rust code split into different modules from a XML schema using the
76/// passed `config`.
77///
78/// Like [`generate`] but instead of returning the whole code as token stream it
79/// returns a [`Module`], holding the code for itself and his sub-modules.
80/// Call [`Module::write_to_files()`] or [`Module::write_to_files_with()`] to
81/// actually create the source code files recursively.
82///
83/// # Errors
84///
85/// Returns a suitable [`Error`] type if the process was not successful.
86#[instrument(err, level = "trace")]
87pub fn generate_modules(config: Config) -> Result<Module, Error> {
88    let schemas = exec_parser(config.parser)?;
89    let types = exec_interpreter(config.interpreter, &schemas)?;
90    let types = exec_optimizer(config.optimizer, types)?;
91    let module = exec_generator_module(config.generator, &schemas, &types)?;
92
93    Ok(module)
94}
95
96/// Executes the [`Parser`] with the passed `config`.
97///
98/// # Errors
99///
100/// Returns a suitable [`Error`] type if the process was not successful.
101#[instrument(err, level = "trace")]
102pub fn exec_parser(config: ParserConfig) -> Result<Schemas, Error> {
103    tracing::info!("Parse Schemas");
104
105    let mut resolver = ManyResolver::new();
106    for r in config.resolver {
107        match r {
108            #[cfg(feature = "web-resolver")]
109            Resolver::Web => {
110                let web_resolver = self::parser::resolver::WebResolver::new();
111
112                resolver = resolver.add_resolver(web_resolver);
113            }
114            Resolver::File => {
115                let file_resolver = FileResolver::new();
116
117                resolver = resolver.add_resolver(file_resolver);
118            }
119        }
120    }
121
122    let mut parser = Parser::new()
123        .with_resolver(resolver)
124        .resolve_includes(config.flags.contains(ParserFlags::RESOLVE_INCLUDES));
125
126    if config.flags.contains(ParserFlags::DEFAULT_NAMESPACES) {
127        parser = parser.with_default_namespaces();
128    }
129
130    for (prefix, namespace) in config.namespaces {
131        parser = parser.with_namespace(prefix, namespace);
132    }
133
134    for schema in config.schemas {
135        match schema {
136            Schema::Url(url) => parser = parser.add_schema_from_url(url)?,
137            Schema::File(path) => parser = parser.add_schema_from_file(path)?,
138            Schema::Schema(schema) => parser = parser.add_schema_from_str(&schema)?,
139        }
140    }
141
142    let schemas = parser.finish();
143
144    if let Some(output) = config.debug_output {
145        let debug = format!("{schemas:#?}");
146
147        write(output, debug)?;
148    }
149
150    Ok(schemas)
151}
152
153/// Executes the [`Interpreter`] with the passed `config` and `schema`.
154///
155/// # Errors
156///
157/// Returns a suitable [`Error`] type if the process was not successful.
158#[instrument(err, level = "trace", skip(schemas))]
159pub fn exec_interpreter(config: InterpreterConfig, schemas: &Schemas) -> Result<Types, Error> {
160    tracing::info!("Interpret Schema");
161
162    let mut interpreter = Interpreter::new(schemas);
163
164    if config.flags.contains(InterpreterFlags::BUILDIN_TYPES) {
165        interpreter = interpreter.with_buildin_types()?;
166    }
167
168    if config.flags.contains(InterpreterFlags::DEFAULT_TYPEDEFS) {
169        interpreter = interpreter.with_default_typedefs()?;
170    }
171
172    if config.flags.contains(InterpreterFlags::WITH_XS_ANY_TYPE) {
173        interpreter = interpreter.with_xs_any_type()?;
174    }
175
176    for (ident, ty) in config.types {
177        let ident = ident.resolve(schemas)?;
178        interpreter = interpreter.with_type(ident, ty)?;
179    }
180
181    let types = interpreter.finish()?;
182
183    if let Some(output) = config.debug_output {
184        let printer = TypesPrinter::new(&types);
185        let debug = format!("{printer}");
186
187        write(output, debug)?;
188    }
189
190    Ok(types)
191}
192
193/// Executes the [`Optimizer`] with the passed `config` and `types`.
194///
195/// # Errors
196///
197/// Returns a suitable [`Error`] type if the process was not successful.
198#[instrument(err, level = "trace", skip(types))]
199pub fn exec_optimizer(config: OptimizerConfig, types: Types) -> Result<Types, Error> {
200    tracing::info!("Optimize Types");
201
202    let mut optimizer = Optimizer::new(types);
203
204    macro_rules! exec {
205        ($flag:ident, $method:ident) => {
206            if config.flags.contains(OptimizerFlags::$flag) {
207                optimizer = optimizer.$method();
208            }
209        };
210    }
211
212    exec!(REMOVE_EMPTY_ENUM_VARIANTS, remove_empty_enum_variants);
213    exec!(REMOVE_EMPTY_ENUMS, remove_empty_enums);
214    exec!(
215        REMOVE_DUPLICATE_UNION_VARIANTS,
216        remove_duplicate_union_variants
217    );
218    exec!(REMOVE_EMPTY_UNIONS, remove_empty_unions);
219    exec!(USE_UNRESTRICTED_BASE_TYPE, use_unrestricted_base_type);
220    exec!(CONVERT_DYNAMIC_TO_CHOICE, convert_dynamic_to_choice);
221    exec!(FLATTEN_COMPLEX_TYPES, flatten_complex_types);
222    exec!(FLATTEN_UNIONS, flatten_unions);
223    exec!(MERGE_ENUM_UNIONS, merge_enum_unions);
224    exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
225    exec!(REMOVE_DUPLICATES, remove_duplicates);
226    exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
227    exec!(MERGE_CHOICE_CARDINALITIES, merge_choice_cardinalities);
228
229    let types = optimizer.finish();
230
231    if let Some(output) = config.debug_output {
232        let printer = TypesPrinter::new(&types);
233        let debug = format!("{printer}");
234
235        write(output, debug)?;
236    }
237
238    Ok(types)
239}
240
241/// Executes the [`Generator`] with the passed `config`, `schema` and `types` to
242/// generate the whole code as token stream.
243///
244/// # Errors
245///
246/// Returns a suitable [`Error`] type if the process was not successful.
247#[instrument(err, level = "trace", skip(schemas, types))]
248pub fn exec_generator(
249    config: GeneratorConfig,
250    schemas: &Schemas,
251    types: &Types,
252) -> Result<TokenStream, Error> {
253    let module = exec_generator_module(config, schemas, types)?;
254    let code = module.to_token_stream();
255
256    Ok(code)
257}
258
259/// Executes the [`Generator`] with the passed `config`, `schema` and `types` to
260/// generate a [`Module`] for further processing.
261///
262/// # Errors
263///
264/// Returns a suitable [`Error`] type if the process was not successful.
265#[instrument(err, level = "trace", skip(schemas, types))]
266pub fn exec_generator_module(
267    config: GeneratorConfig,
268    schemas: &Schemas,
269    types: &Types,
270) -> Result<Module, Error> {
271    tracing::info!("Generate Module");
272
273    let mut generator = Generator::new(types)
274        .flags(config.flags)
275        .box_flags(config.box_flags)
276        .typedef_mode(config.typedef_mode)
277        .serde_support(config.serde_support)
278        .xsd_parser_crate(config.xsd_parser);
279
280    if let Some(derive) = config.derive {
281        generator = generator.derive(derive);
282    }
283
284    if let Some(traits) = config.dyn_type_traits {
285        generator = generator.dyn_type_traits(traits)?;
286    }
287
288    generator = generator.with_type_postfix(IdentType::Type, config.type_postfix.type_);
289    generator = generator.with_type_postfix(IdentType::Element, config.type_postfix.element);
290    generator =
291        generator.with_type_postfix(IdentType::ElementType, config.type_postfix.element_type);
292
293    for triple in config.types {
294        let ident = triple.resolve(schemas)?;
295
296        generator = generator.with_type(ident)?;
297    }
298
299    for renderer in config.renderers {
300        match renderer {
301            Renderer::Types => generator = generator.with_renderer(TypesRenderer),
302            Renderer::Defaults => generator = generator.with_renderer(DefaultsRenderer),
303            Renderer::NamespaceConstants => {
304                generator = generator.with_renderer(NamespaceConstantsRenderer);
305            }
306            Renderer::WithNamespaceTrait => {
307                generator = generator.with_renderer(WithNamespaceTraitRenderer);
308            }
309            Renderer::QuickXmlSerialize => {
310                generator = generator.with_renderer(QuickXmlSerializeRenderer);
311            }
312            Renderer::QuickXmlDeserialize { boxed_deserializer } => {
313                generator =
314                    generator.with_renderer(QuickXmlDeserializeRenderer { boxed_deserializer });
315            }
316        }
317    }
318
319    let mut generator = generator.into_fixed();
320    match config.generate {
321        Generate::All => generator = generator.generate_all_types()?,
322        Generate::Named => generator = generator.generate_named_types()?,
323        Generate::Types(idents) => {
324            for triple in idents {
325                let ident = triple.resolve(schemas)?;
326
327                generator = generator.generate_type(ident)?;
328            }
329        }
330    }
331
332    let module = generator.into_module();
333
334    Ok(module)
335}