1#![recursion_limit = "256"]
2#![allow(clippy::doc_markdown)]
3#![doc = include_str!(concat!(env!("OUT_DIR"), "/README.md"))]
4
5pub mod config;
6pub mod misc;
7pub mod models;
8pub mod pipeline;
9pub mod traits;
10
11mod macros;
12mod meta_types_printer;
13
14pub type RendererError = self::pipeline::renderer::Error;
16
17pub type GeneratorError = self::pipeline::generator::Error;
19
20pub type InterpreterError = self::pipeline::interpreter::Error;
22
23pub 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 proc_macro2::Ident as Ident2;
31
32pub use self::config::Config;
33pub use self::meta_types_printer::MetaTypesPrinter;
34pub use self::models::{
35 code::{Module, SubModules},
36 data::DataTypes,
37 meta::MetaTypes,
38 schema::Schemas,
39 IdentCache, IdentType, Name, TypeIdent,
40};
41pub use self::pipeline::{
42 generator::Generator,
43 interpreter::Interpreter,
44 optimizer::Optimizer,
45 parser::Parser,
46 renderer::{
47 DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlDeserializeRenderStep,
48 QuickXmlSerializeRenderStep, RenderStep, Renderer, SerdeQuickXmlTypesRenderStep,
49 SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep,
50 WithNamespaceTraitRenderStep,
51 },
52};
53pub use self::traits::{VecHelper, WithIdent};
54
55use anyhow::Error as AnyError;
56use proc_macro2::TokenStream;
57use quote::ToTokens;
58use thiserror::Error as ThisError;
59use tracing::instrument;
60
61use self::config::{
62 Generate, GeneratorConfig, InterpreterConfig, InterpreterFlags, OptimizerConfig,
63 OptimizerFlags, ParserConfig, ParserFlags, RendererConfig, Resolver, Schema,
64};
65use self::macros::{assert, unreachable};
66use self::pipeline::{
67 optimizer::UnrestrictedBaseFlags,
68 parser::resolver::{FileResolver, ManyResolver},
69};
70
71#[instrument(err, level = "trace")]
85pub fn generate(config: Config) -> Result<TokenStream, Error> {
86 let module = generate_modules(config)?;
87 let code = module.to_token_stream();
88
89 Ok(code)
90}
91
92#[instrument(err, level = "trace")]
104pub fn generate_modules(config: Config) -> Result<Module, Error> {
105 let schemas = exec_parser(config.parser)?;
106 let (meta_types, ident_cache) =
107 exec_interpreter_with_ident_cache(config.interpreter, &schemas)?;
108 let meta_types = exec_optimizer(config.optimizer, meta_types)?;
109 let data_types = exec_generator_with_ident_cache(
110 config.generator,
111 &schemas,
112 Some(&ident_cache),
113 &meta_types,
114 )?;
115 let module = exec_render(config.renderer, &data_types)?;
116
117 Ok(module)
118}
119
120#[instrument(err, level = "trace")]
126pub fn exec_parser(config: ParserConfig) -> Result<Schemas, Error> {
127 tracing::info!("Parse Schemas");
128
129 let mut resolver = ManyResolver::new();
130 for r in config.resolver {
131 match r {
132 #[cfg(feature = "web-resolver")]
133 Resolver::Web => {
134 let web_resolver = self::pipeline::parser::resolver::WebResolver::new();
135
136 resolver = resolver.add_resolver(web_resolver);
137 }
138 Resolver::File => {
139 let file_resolver = FileResolver::new();
140
141 resolver = resolver.add_resolver(file_resolver);
142 }
143 }
144 }
145
146 let mut parser = Parser::new()
147 .with_resolver(resolver)
148 .resolve_includes(config.flags.contains(ParserFlags::RESOLVE_INCLUDES))
149 .generate_prefixes(config.flags.contains(ParserFlags::GENERATE_PREFIXES))
150 .alternative_prefixes(config.flags.contains(ParserFlags::ALTERNATIVE_PREFIXES));
151
152 if config.flags.contains(ParserFlags::DEFAULT_NAMESPACES) {
153 parser = parser.with_default_namespaces();
154 }
155
156 for (prefix, namespace) in config.namespaces {
157 parser = parser.with_namespace(prefix, namespace);
158 }
159
160 for schema in config.schemas {
161 match schema {
162 Schema::Url(url) => parser = parser.add_schema_from_url(url)?,
163 Schema::File(path) => parser = parser.add_schema_from_file(path)?,
164 Schema::Schema(schema) => parser = parser.add_schema_from_str(&schema)?,
165 Schema::NamedSchema(name, schema) => {
166 parser = parser.add_named_schema_from_str(name, &schema)?;
167 }
168 }
169 }
170
171 let schemas = parser.finish();
172
173 if let Some(output) = config.debug_output {
174 let debug = format!("{schemas:#?}");
175
176 write(output, debug)?;
177 }
178
179 Ok(schemas)
180}
181
182#[instrument(err, level = "trace", skip(schemas))]
188pub fn exec_interpreter(config: InterpreterConfig, schemas: &Schemas) -> Result<MetaTypes, Error> {
189 exec_interpreter_with_ident_cache(config, schemas).map(|(types, _)| types)
190}
191
192#[instrument(err, level = "trace", skip(schemas))]
201pub fn exec_interpreter_with_ident_cache(
202 config: InterpreterConfig,
203 schemas: &Schemas,
204) -> Result<(MetaTypes, IdentCache), Error> {
205 tracing::info!("Interpret Schema");
206
207 let mut interpreter = Interpreter::new(schemas);
208
209 macro_rules! eval_flag {
210 ($flag:ident, $fn:ident) => {
211 if config.flags.contains(InterpreterFlags::$flag) {
212 interpreter = interpreter.$fn()?;
213 }
214 };
215 }
216
217 if let Some(naming) = config.naming {
218 interpreter = interpreter.with_naming_boxed(naming);
219 }
220
221 eval_flag!(BUILDIN_TYPES, with_buildin_types);
222 eval_flag!(DEFAULT_TYPEDEFS, with_default_typedefs);
223 eval_flag!(WITH_XS_ANY_TYPE, with_xs_any_type);
224 eval_flag!(WITH_XS_ANY_SIMPLE_TYPE, with_xs_any_simple_type);
225 eval_flag!(WITH_NUM_BIG_INT, with_num_big_int);
226 eval_flag!(NONZERO_TYPEDEFS, with_nonzero_typedefs);
227
228 for (ident, ty) in config.types {
229 let ident = ident.resolve(schemas)?;
230 interpreter = interpreter.with_type(ident, ty)?;
231 }
232
233 let (types, ident_cache) = interpreter.finish()?;
234
235 if let Some(output) = config.debug_output {
236 let printer = MetaTypesPrinter::new(&types);
237 let debug = format!("{printer}");
238
239 write(output, debug)?;
240 }
241
242 Ok((types, ident_cache))
243}
244
245#[instrument(err, level = "trace", skip(types))]
251pub fn exec_optimizer(config: OptimizerConfig, types: MetaTypes) -> Result<MetaTypes, Error> {
252 tracing::info!("Optimize Types");
253
254 let mut optimizer = Optimizer::new(types);
255 let mut unrestricted_base = UnrestrictedBaseFlags::empty();
256
257 macro_rules! exec {
258 ($flag:ident, $method:ident) => {
259 if config.flags.contains(OptimizerFlags::$flag) {
260 optimizer = optimizer.$method();
261 }
262 };
263 }
264
265 macro_rules! unrestricted_base {
266 ($a:ident, $b:ident) => {
267 if config.flags.contains(OptimizerFlags::$a) {
268 unrestricted_base |= UnrestrictedBaseFlags::$b;
269 }
270 };
271 }
272
273 unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_COMPLEX, COMPLEX);
274 unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_SIMPLE, SIMPLE);
275 unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_ENUM, ENUM);
276 unrestricted_base!(USE_UNRESTRICTED_BASE_TYPE_UNION, UNION);
277
278 if !unrestricted_base.is_empty() {
279 optimizer = optimizer.use_unrestricted_base_type(unrestricted_base);
280 }
281
282 exec!(
283 REPLACE_XS_ANY_TYPE_WITH_ANY_ELEMENT,
284 replace_xs_any_type_with_any_element
285 );
286 exec!(REMOVE_EMPTY_ENUM_VARIANTS, remove_empty_enum_variants);
287 exec!(REMOVE_EMPTY_ENUMS, remove_empty_enums);
288 exec!(CONVERT_DYNAMIC_TO_CHOICE, convert_dynamic_to_choice);
289 exec!(FLATTEN_COMPLEX_TYPES, flatten_complex_types);
290 exec!(FLATTEN_UNIONS, flatten_unions);
291 exec!(MERGE_ENUM_UNIONS, merge_enum_unions);
292 exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
293 exec!(
294 REMOVE_DUPLICATE_UNION_VARIANTS,
295 remove_duplicate_union_variants
296 );
297 exec!(REMOVE_EMPTY_UNIONS, remove_empty_unions);
298 exec!(REMOVE_DUPLICATES, remove_duplicates);
299 exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
300 exec!(REMOVE_EMPTY_ENUM_VARIANTS, remove_empty_enum_variants);
301 exec!(REMOVE_EMPTY_ENUMS, remove_empty_enums);
302 exec!(
303 REMOVE_DUPLICATE_UNION_VARIANTS,
304 remove_duplicate_union_variants
305 );
306 exec!(REMOVE_EMPTY_UNIONS, remove_empty_unions);
307 exec!(MERGE_CHOICE_CARDINALITIES, merge_choice_cardinalities);
308 exec!(SIMPLIFY_MIXED_TYPES, simplify_mixed_types);
309
310 let types = optimizer.finish();
311
312 if let Some(output) = config.debug_output {
313 let printer = MetaTypesPrinter::new(&types);
314 let debug = format!("{printer}");
315
316 write(output, debug)?;
317 }
318
319 Ok(types)
320}
321
322#[instrument(err, level = "trace", skip(schemas, types))]
329pub fn exec_generator<'types>(
330 config: GeneratorConfig,
331 schemas: &Schemas,
332 types: &'types MetaTypes,
333) -> Result<DataTypes<'types>, Error> {
334 exec_generator_with_ident_cache(config, schemas, None, types)
335}
336
337#[instrument(err, level = "trace", skip(schemas, types))]
348pub fn exec_generator_with_ident_cache<'types>(
349 config: GeneratorConfig,
350 schemas: &Schemas,
351 ident_cache: Option<&IdentCache>,
352 types: &'types MetaTypes,
353) -> Result<DataTypes<'types>, Error> {
354 tracing::info!("Generate Module");
355
356 let mut generator = Generator::new(types)
357 .flags(config.flags)
358 .box_flags(config.box_flags)
359 .typedef_mode(config.typedef_mode);
360
361 generator = generator
362 .text_type(config.text_type)
363 .map_err(GeneratorError::from)?;
364 generator = generator
365 .mixed_type(config.mixed_type)
366 .map_err(GeneratorError::from)?;
367 generator = generator
368 .nillable_type(config.nillable_type)
369 .map_err(GeneratorError::from)?;
370 generator = generator
371 .any_type(config.any_type)
372 .map_err(GeneratorError::from)?;
373 generator = generator
374 .any_attributes_type(config.any_attributes_type)
375 .map_err(GeneratorError::from)?;
376
377 generator = generator.with_type_postfix(IdentType::Type, config.type_postfix.type_);
378 generator = generator.with_type_postfix(IdentType::Element, config.type_postfix.element);
379 generator =
380 generator.with_type_postfix(IdentType::ElementType, config.type_postfix.element_type);
381 generator = generator.with_type_postfix(
382 IdentType::NillableContent,
383 config.type_postfix.nillable_content,
384 );
385 generator = generator.with_type_postfix(
386 IdentType::DynamicElement,
387 config.type_postfix.dynamic_element,
388 );
389
390 for triple in config.types {
391 let mut ident = triple.resolve(schemas)?;
392 if let Some(ident_cache) = &ident_cache {
393 ident = ident_cache.resolve(ident)?;
394 }
395
396 generator = generator.with_type(ident)?;
397 }
398
399 let mut generator = generator.into_fixed();
400 match config.generate {
401 Generate::All => generator = generator.generate_all_types()?,
402 Generate::Named => generator = generator.generate_named_types()?,
403 Generate::Types(idents) => {
404 for triple in idents {
405 let ident = triple.resolve(schemas)?;
406
407 generator = generator.generate_type(ident)?;
408 }
409 }
410 }
411
412 let data_types = generator.finish();
413
414 Ok(data_types)
415}
416
417pub fn exec_render(config: RendererConfig, types: &DataTypes<'_>) -> Result<Module, RendererError> {
424 tracing::info!("Render Module");
425
426 let mut renderer = Renderer::new(types)
427 .flags(config.flags)
428 .alloc_crate(config.alloc)
429 .xsd_parser_types(config.xsd_parser_types);
430
431 if let Some(derive) = config.derive {
432 renderer = renderer.derive(derive);
433 }
434
435 if let Some(traits) = config.dyn_type_traits {
436 renderer = renderer.dyn_type_traits(traits)?;
437 }
438
439 for step in config.steps {
440 renderer = renderer.with_step_boxed(step.into_render_step());
441 }
442
443 let module = renderer.finish();
444
445 Ok(module)
446}
447
448#[derive(Debug, ThisError)]
450pub enum Error {
451 #[error("IO Error: {0}")]
453 IoError(
454 #[from]
455 #[source]
456 IoError,
457 ),
458
459 #[error("Parser error: {0}")]
461 ParserError(#[source] ParserError<AnyError>),
462
463 #[error("Interpreter error: {0}")]
465 InterpreterError(
466 #[from]
467 #[source]
468 InterpreterError,
469 ),
470
471 #[error("Generator error: {0}")]
473 GeneratorError(
474 #[from]
475 #[source]
476 GeneratorError,
477 ),
478
479 #[error("Renderer error: {0}")]
481 RendererError(
482 #[from]
483 #[source]
484 RendererError,
485 ),
486}
487
488impl<E> From<ParserError<E>> for Error
489where
490 AnyError: From<E>,
491{
492 fn from(value: ParserError<E>) -> Self {
493 match value {
494 ParserError::IoError(err) => Self::ParserError(ParserError::IoError(err)),
495 ParserError::XmlError(err) => Self::ParserError(ParserError::XmlError(err)),
496 ParserError::UrlParseError(err) => Self::ParserError(ParserError::UrlParseError(err)),
497 ParserError::UnableToResolve(url) => {
498 Self::ParserError(ParserError::UnableToResolve(url))
499 }
500 ParserError::Resolver(err) => {
501 Self::ParserError(ParserError::Resolver(AnyError::from(err)))
502 }
503 ParserError::InvalidFilePath(path) => {
504 Self::ParserError(ParserError::InvalidFilePath(path))
505 }
506 ParserError::MismatchingTargetNamespace {
507 location,
508 found,
509 expected,
510 } => Self::ParserError(ParserError::MismatchingTargetNamespace {
511 location,
512 found,
513 expected,
514 }),
515 }
516 }
517}