xsd_parser/
lib.rs

1#![recursion_limit = "256"]
2#![doc = include_str!(concat!(env!("OUT_DIR"), "/README.md"))]
3
4pub mod config;
5pub mod models;
6pub mod pipeline;
7pub mod quick_xml;
8pub mod xml;
9
10mod macros;
11mod meta_types_printer;
12mod traits;
13
14/// Type alias for [`pipeline::renderer::Error`].
15pub type RendererError = self::pipeline::renderer::Error;
16
17/// Type alias for [`pipeline::generator::Error`].
18pub type GeneratorError = self::pipeline::generator::Error;
19
20/// Type alias for [`pipeline::interpreter::Error`].
21pub type InterpreterError = self::pipeline::interpreter::Error;
22
23/// Type alias for [`pipeline::parser::Error`].
24pub type ParserError<E> = self::pipeline::parser::Error<E>;
25
26use std::fmt::Debug;
27use std::fs::write;
28use std::io::Error as IoError;
29
30pub use self::config::Config;
31pub use self::meta_types_printer::MetaTypesPrinter;
32pub use self::models::{
33    code::{Module, SubModules},
34    data::DataTypes,
35    meta::MetaTypes,
36    schema::Schemas,
37    Ident, IdentType, Name,
38};
39pub use self::pipeline::{
40    generator::Generator,
41    interpreter::Interpreter,
42    optimizer::Optimizer,
43    parser::Parser,
44    renderer::{
45        DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlDeserializeRenderStep,
46        QuickXmlSerializeRenderStep, RenderStep, Renderer, SerdeQuickXmlTypesRenderStep,
47        SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep,
48        WithNamespaceTraitRenderStep,
49    },
50};
51pub use self::traits::{AsAny, VecHelper, WithIdent, WithNamespace};
52
53use anyhow::Error as AnyError;
54use proc_macro2::TokenStream;
55use quote::ToTokens;
56use thiserror::Error as ThisError;
57use tracing::instrument;
58
59use self::config::{
60    Generate, GeneratorConfig, InterpreterConfig, InterpreterFlags, OptimizerConfig,
61    OptimizerFlags, ParserConfig, ParserFlags, RendererConfig, Resolver, Schema,
62};
63use self::macros::{assert, assert_eq, unreachable};
64use self::pipeline::{
65    optimizer::UnrestrictedBaseFlags,
66    parser::resolver::{FileResolver, ManyResolver},
67};
68
69/// Generates rust code from a XML schema using the passed `config`.
70///
71/// This is the most easiest way to use the `xsd-parser` crate. The `generate`
72/// function provides a simple way to generate rust code from XML schemas using
73/// the passed configuration.
74///
75/// If you need more detailed control over the generation process or only a part
76/// of it, use the [`Parser`], [`Interpreter`], [`Optimizer`] or [`Generator`]
77/// directly.
78///
79/// # Errors
80///
81/// Returns a suitable [`Error`] type if the process was not successful.
82#[instrument(err, level = "trace")]
83pub fn generate(config: Config) -> Result<TokenStream, Error> {
84    let module = generate_modules(config)?;
85    let code = module.to_token_stream();
86
87    Ok(code)
88}
89
90/// Generates rust code split into different modules from a XML schema using the
91/// passed `config`.
92///
93/// Like [`generate`] but instead of returning the whole code as token stream it
94/// returns a [`Module`], holding the code for itself and his sub-modules.
95/// Call [`Module::write_to_files()`] or [`Module::write_to_files_with()`] to
96/// actually create the source code files recursively.
97///
98/// # Errors
99///
100/// Returns a suitable [`Error`] type if the process was not successful.
101#[instrument(err, level = "trace")]
102pub fn generate_modules(config: Config) -> Result<Module, Error> {
103    let schemas = exec_parser(config.parser)?;
104    let meta_types = exec_interpreter(config.interpreter, &schemas)?;
105    let meta_types = exec_optimizer(config.optimizer, meta_types)?;
106    let data_types = exec_generator(config.generator, &schemas, &meta_types)?;
107    let module = exec_render(config.renderer, &data_types)?;
108
109    Ok(module)
110}
111
112/// Executes the [`Parser`] with the passed `config`.
113///
114/// # Errors
115///
116/// Returns a suitable [`Error`] type if the process was not successful.
117#[instrument(err, level = "trace")]
118pub fn exec_parser(config: ParserConfig) -> Result<Schemas, Error> {
119    tracing::info!("Parse Schemas");
120
121    let mut resolver = ManyResolver::new();
122    for r in config.resolver {
123        match r {
124            #[cfg(feature = "web-resolver")]
125            Resolver::Web => {
126                let web_resolver = self::pipeline::parser::resolver::WebResolver::new();
127
128                resolver = resolver.add_resolver(web_resolver);
129            }
130            Resolver::File => {
131                let file_resolver = FileResolver::new();
132
133                resolver = resolver.add_resolver(file_resolver);
134            }
135        }
136    }
137
138    let mut parser = Parser::new()
139        .with_resolver(resolver)
140        .resolve_includes(config.flags.contains(ParserFlags::RESOLVE_INCLUDES));
141
142    if config.flags.contains(ParserFlags::DEFAULT_NAMESPACES) {
143        parser = parser.with_default_namespaces();
144    }
145
146    for (prefix, namespace) in config.namespaces {
147        parser = parser.with_namespace(prefix, namespace);
148    }
149
150    for schema in config.schemas {
151        match schema {
152            Schema::Url(url) => parser = parser.add_schema_from_url(url)?,
153            Schema::File(path) => parser = parser.add_schema_from_file(path)?,
154            Schema::Schema(schema) => parser = parser.add_schema_from_str(&schema)?,
155            Schema::NamedSchema(name, schema) => {
156                parser = parser.add_named_schema_from_str(name, &schema)?;
157            }
158        }
159    }
160
161    let schemas = parser.finish();
162
163    if let Some(output) = config.debug_output {
164        let debug = format!("{schemas:#?}");
165
166        write(output, debug)?;
167    }
168
169    Ok(schemas)
170}
171
172/// Executes the [`Interpreter`] with the passed `config` and `schema`.
173///
174/// # Errors
175///
176/// Returns a suitable [`Error`] type if the process was not successful.
177#[instrument(err, level = "trace", skip(schemas))]
178pub fn exec_interpreter(config: InterpreterConfig, schemas: &Schemas) -> Result<MetaTypes, Error> {
179    tracing::info!("Interpret Schema");
180
181    let mut interpreter = Interpreter::new(schemas);
182
183    if config.flags.contains(InterpreterFlags::BUILDIN_TYPES) {
184        interpreter = interpreter.with_buildin_types()?;
185    }
186
187    if config.flags.contains(InterpreterFlags::DEFAULT_TYPEDEFS) {
188        interpreter = interpreter.with_default_typedefs()?;
189    }
190
191    if config.flags.contains(InterpreterFlags::WITH_XS_ANY_TYPE) {
192        interpreter = interpreter.with_xs_any_type()?;
193    }
194
195    if config.flags.contains(InterpreterFlags::WITH_NUM_BIG_INT) {
196        interpreter = interpreter.with_num_big_int()?;
197    }
198
199    for (ident, ty) in config.types {
200        let ident = ident.resolve(schemas)?;
201        interpreter = interpreter.with_type(ident, ty)?;
202    }
203
204    let types = interpreter.finish()?;
205
206    if let Some(output) = config.debug_output {
207        let printer = MetaTypesPrinter::new(&types);
208        let debug = format!("{printer}");
209
210        write(output, debug)?;
211    }
212
213    Ok(types)
214}
215
216/// Executes the [`Optimizer`] with the passed `config` and `types`.
217///
218/// # Errors
219///
220/// Returns a suitable [`Error`] type if the process was not successful.
221#[instrument(err, level = "trace", skip(types))]
222pub fn exec_optimizer(config: OptimizerConfig, types: MetaTypes) -> Result<MetaTypes, Error> {
223    tracing::info!("Optimize Types");
224
225    let mut optimizer = Optimizer::new(types);
226    let mut unrestricted_base = UnrestrictedBaseFlags::empty();
227
228    macro_rules! exec {
229        ($flag:ident, $method:ident) => {
230            if config.flags.contains(OptimizerFlags::$flag) {
231                optimizer = optimizer.$method();
232            }
233        };
234    }
235
236    macro_rules! unrestricted_base {
237        ($a:ident, $b:ident) => {
238            if config.flags.contains(OptimizerFlags::$a) {
239                unrestricted_base |= UnrestrictedBaseFlags::$b;
240            }
241        };
242    }
243
244    unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_COMPLEX, COMPLEX);
245    unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_SIMPLE, SIMPLE);
246    unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_ENUM, ENUM);
247    unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_UNION, UNION);
248
249    if !unrestricted_base.is_empty() {
250        optimizer = optimizer.use_unrestricted_base_type(unrestricted_base);
251    }
252
253    exec!(REMOVE_EMPTY_ENUM_VARIANTS, remove_empty_enum_variants);
254    exec!(REMOVE_EMPTY_ENUMS, remove_empty_enums);
255    exec!(
256        REMOVE_DUPLICATE_UNION_VARIANTS,
257        remove_duplicate_union_variants
258    );
259    exec!(REMOVE_EMPTY_UNIONS, remove_empty_unions);
260    exec!(CONVERT_DYNAMIC_TO_CHOICE, convert_dynamic_to_choice);
261    exec!(FLATTEN_COMPLEX_TYPES, flatten_complex_types);
262    exec!(FLATTEN_UNIONS, flatten_unions);
263    exec!(MERGE_ENUM_UNIONS, merge_enum_unions);
264    exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
265    exec!(REMOVE_DUPLICATES, remove_duplicates);
266    exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
267    exec!(MERGE_CHOICE_CARDINALITIES, merge_choice_cardinalities);
268    exec!(SIMPLIFY_MIXED_TYPES, simplify_mixed_types);
269
270    let types = optimizer.finish();
271
272    if let Some(output) = config.debug_output {
273        let printer = MetaTypesPrinter::new(&types);
274        let debug = format!("{printer}");
275
276        write(output, debug)?;
277    }
278
279    Ok(types)
280}
281
282/// Executes the [`Generator`] with the passed `config`, `schema` and `types` to
283/// generate a [`DataTypes`] for further processing.
284///
285/// # Errors
286///
287/// Returns a suitable [`Error`] type if the process was not successful.
288#[instrument(err, level = "trace", skip(schemas, types))]
289pub fn exec_generator<'types>(
290    config: GeneratorConfig,
291    schemas: &Schemas,
292    types: &'types MetaTypes,
293) -> Result<DataTypes<'types>, Error> {
294    tracing::info!("Generate Module");
295
296    let mut generator = Generator::new(types)
297        .flags(config.flags)
298        .box_flags(config.box_flags)
299        .typedef_mode(config.typedef_mode);
300
301    if let Some(any_type) = config.any_type {
302        generator = generator.any_type(any_type).map_err(GeneratorError::from)?;
303    }
304
305    if let Some(any_attribute_type) = config.any_attribute_type {
306        generator = generator
307            .any_attribute_type(any_attribute_type)
308            .map_err(GeneratorError::from)?;
309    }
310
311    generator = generator.with_type_postfix(IdentType::Type, config.type_postfix.type_);
312    generator = generator.with_type_postfix(IdentType::Element, config.type_postfix.element);
313    generator =
314        generator.with_type_postfix(IdentType::ElementType, config.type_postfix.element_type);
315
316    for triple in config.types {
317        let ident = triple.resolve(schemas)?;
318
319        generator = generator.with_type(ident)?;
320    }
321
322    let mut generator = generator.into_fixed();
323    match config.generate {
324        Generate::All => generator = generator.generate_all_types()?,
325        Generate::Named => generator = generator.generate_named_types()?,
326        Generate::Types(idents) => {
327            for triple in idents {
328                let ident = triple.resolve(schemas)?;
329
330                generator = generator.generate_type(ident)?;
331            }
332        }
333    }
334
335    let data_types = generator.finish();
336
337    Ok(data_types)
338}
339
340/// Executes the rendering process using the passed `config` and the `types`
341/// created by the [`Generator`].
342///
343/// # Errors
344///
345/// Returns a suitable [`Error`] type if the process was not successful.
346pub fn exec_render(config: RendererConfig, types: &DataTypes<'_>) -> Result<Module, RendererError> {
347    let mut renderer = Renderer::new(types)
348        .flags(config.flags)
349        .xsd_parser_crate(config.xsd_parser);
350
351    if let Some(derive) = config.derive {
352        renderer = renderer.derive(derive);
353    }
354
355    if let Some(traits) = config.dyn_type_traits {
356        renderer = renderer.dyn_type_traits(traits)?;
357    }
358
359    for step in config.steps {
360        renderer = renderer.with_step_boxed(step.into_render_step());
361    }
362
363    let module = renderer.finish();
364
365    Ok(module)
366}
367
368/// Error emitted by the [`generate`] function.
369#[derive(Debug, ThisError)]
370pub enum Error {
371    /// IO Error.
372    #[error("IO Error: {0}")]
373    IoError(
374        #[from]
375        #[source]
376        IoError,
377    ),
378
379    /// Parser error.
380    #[error("Parser error: {0}")]
381    ParserError(#[source] ParserError<AnyError>),
382
383    /// Interpreter error.
384    #[error("Interpreter error: {0}")]
385    InterpreterError(
386        #[from]
387        #[source]
388        InterpreterError,
389    ),
390
391    /// Generator error.
392    #[error("Generator error: {0}")]
393    GeneratorError(
394        #[from]
395        #[source]
396        GeneratorError,
397    ),
398
399    /// Renderer error.
400    #[error("Renderer error: {0}")]
401    RendererError(
402        #[from]
403        #[source]
404        RendererError,
405    ),
406}
407
408impl<E> From<ParserError<E>> for Error
409where
410    AnyError: From<E>,
411{
412    fn from(value: ParserError<E>) -> Self {
413        match value {
414            ParserError::IoError(err) => Self::ParserError(ParserError::IoError(err)),
415            ParserError::XmlError(err) => Self::ParserError(ParserError::XmlError(err)),
416            ParserError::UrlParseError(err) => Self::ParserError(ParserError::UrlParseError(err)),
417            ParserError::UnableToResolve(url) => {
418                Self::ParserError(ParserError::UnableToResolve(url))
419            }
420            ParserError::Resolver(err) => {
421                Self::ParserError(ParserError::Resolver(AnyError::from(err)))
422            }
423            ParserError::InvalidFilePath(path) => {
424                Self::ParserError(ParserError::InvalidFilePath(path))
425            }
426        }
427    }
428}