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