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
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 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#[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#[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#[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#[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#[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#[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
340pub 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#[derive(Debug, ThisError)]
370pub enum Error {
371 #[error("IO Error: {0}")]
373 IoError(
374 #[from]
375 #[source]
376 IoError,
377 ),
378
379 #[error("Parser error: {0}")]
381 ParserError(#[source] ParserError<AnyError>),
382
383 #[error("Interpreter error: {0}")]
385 InterpreterError(
386 #[from]
387 #[source]
388 InterpreterError,
389 ),
390
391 #[error("Generator error: {0}")]
393 GeneratorError(
394 #[from]
395 #[source]
396 GeneratorError,
397 ),
398
399 #[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}