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
12pub type RendererError = self::pipeline::renderer::Error;
14
15pub type GeneratorError = self::pipeline::generator::Error;
17
18pub type InterpreterError = self::pipeline::interpreter::Error;
20
21pub 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#[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#[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#[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#[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#[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#[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
362pub 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#[derive(Debug, ThisError)]
395pub enum Error {
396 #[error("IO Error: {0}")]
398 IoError(
399 #[from]
400 #[source]
401 IoError,
402 ),
403
404 #[error("Parser error: {0}")]
406 ParserError(#[source] ParserError<AnyError>),
407
408 #[error("Interpreter error: {0}")]
410 InterpreterError(
411 #[from]
412 #[source]
413 InterpreterError,
414 ),
415
416 #[error("Generator error: {0}")]
418 GeneratorError(
419 #[from]
420 #[source]
421 GeneratorError,
422 ),
423
424 #[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}