xsd_parser/
lib.rs

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