1#![doc = include_str!(concat!(env!("OUT_DIR"), "/README.md"))]
2
3pub mod config;
4pub mod generator;
5pub mod interpreter;
6pub mod optimizer;
7pub mod parser;
8pub mod quick_xml;
9pub mod schema;
10pub mod types;
11
12mod macros;
13mod misc;
14
15pub type GeneratorError = generator::Error;
17
18pub type InterpreterError = interpreter::Error;
20
21pub type ParserError<E> = parser::Error<E>;
23
24use std::fs::write;
25
26pub use config::Config;
27pub use generator::Generator;
28pub use interpreter::Interpreter;
29pub use misc::{AsAny, Error, WithNamespace};
30pub use optimizer::Optimizer;
31pub use parser::Parser;
32
33use macros::{assert_eq, unreachable};
34use proc_macro2::TokenStream;
35use schema::{NamespacePrefix, Schemas};
36use tracing::instrument;
37
38use self::config::{
39 Generate, GeneratorConfig, InterpreterConfig, InterpreterFlags, OptimizerConfig,
40 OptimizerFlags, ParserConfig, ParserFlags, Resolver, Schema,
41};
42use self::misc::TypesPrinter;
43use self::parser::resolver::{FileResolver, ManyResolver};
44use self::types::{Ident, IdentType, Name, Types};
45
46#[instrument(err, level = "trace")]
60pub fn generate(config: Config) -> Result<TokenStream, Error> {
61 let schemas = exec_parser(config.parser)?;
62 let types = exec_interpreter(config.interpreter, &schemas)?;
63 let types = exec_optimizer(config.optimizer, types)?;
64 let code = exec_generator(config.generator, &schemas, &types)?;
65
66 Ok(code)
67}
68
69#[instrument(err, level = "trace")]
75pub fn exec_parser(config: ParserConfig) -> Result<Schemas, Error> {
76 tracing::info!("Parse Schemas");
77
78 let mut resolver = ManyResolver::new();
79 for r in config.resolver {
80 match r {
81 #[cfg(feature = "web-resolver")]
82 Resolver::Web => {
83 let web_resolver = self::parser::resolver::WebResolver::new();
84
85 resolver = resolver.add_resolver(web_resolver);
86 }
87 Resolver::File => {
88 let file_resolver = FileResolver::new();
89
90 resolver = resolver.add_resolver(file_resolver);
91 }
92 }
93 }
94
95 let mut parser = Parser::new()
96 .with_resolver(resolver)
97 .resolve_includes(config.flags.contains(ParserFlags::RESOLVE_INCLUDES));
98
99 if config.flags.contains(ParserFlags::DEFAULT_NAMESPACES) {
100 parser = parser.with_default_namespaces();
101 }
102
103 for (prefix, namespace) in config.namespaces {
104 parser = parser.with_namespace(prefix, namespace);
105 }
106
107 for schema in config.schemas {
108 match schema {
109 Schema::Url(url) => parser = parser.add_schema_from_url(url)?,
110 Schema::File(path) => parser = parser.add_schema_from_file(path)?,
111 Schema::Schema(schema) => parser = parser.add_schema_from_str(&schema)?,
112 }
113 }
114
115 let schemas = parser.finish();
116
117 if let Some(output) = config.debug_output {
118 let debug = format!("{schemas:#?}");
119
120 write(output, debug)?;
121 }
122
123 Ok(schemas)
124}
125
126#[instrument(err, level = "trace", skip(schemas))]
132pub fn exec_interpreter(config: InterpreterConfig, schemas: &Schemas) -> Result<Types, Error> {
133 tracing::info!("Interpret Schema");
134
135 let mut interpreter = Interpreter::new(schemas);
136
137 if config.flags.contains(InterpreterFlags::BUILDIN_TYPES) {
138 interpreter = interpreter.with_buildin_types()?;
139 }
140
141 if config.flags.contains(InterpreterFlags::DEFAULT_TYPEDEFS) {
142 interpreter = interpreter.with_default_typedefs()?;
143 }
144
145 if config.flags.contains(InterpreterFlags::WITH_XS_ANY_TYPE) {
146 interpreter = interpreter.with_xs_any_type()?;
147 }
148
149 for (x, ident, type_) in config.types {
150 let ident = resolve_ident(schemas, &ident, x)?;
151 interpreter = interpreter.with_type(ident, type_)?;
152 }
153
154 let types = interpreter.finish()?;
155
156 if let Some(output) = config.debug_output {
157 let printer = TypesPrinter::new(&types);
158 let debug = format!("{printer}");
159
160 write(output, debug)?;
161 }
162
163 Ok(types)
164}
165
166#[instrument(err, level = "trace", skip(types))]
172pub fn exec_optimizer(config: OptimizerConfig, types: Types) -> Result<Types, Error> {
173 tracing::info!("Optimize Types");
174
175 let mut optimizer = Optimizer::new(types);
176
177 macro_rules! exec {
178 ($flag:ident, $method:ident) => {
179 if config.flags.contains(OptimizerFlags::$flag) {
180 optimizer = optimizer.$method();
181 }
182 };
183 }
184
185 exec!(REMOVE_EMPTY_ENUM_VARIANTS, remove_empty_enum_variants);
186 exec!(REMOVE_EMPTY_ENUMS, remove_empty_enums);
187 exec!(
188 REMOVE_DUPLICATE_UNION_VARIANTS,
189 remove_duplicate_union_variants
190 );
191 exec!(REMOVE_EMPTY_UNIONS, remove_empty_unions);
192 exec!(USE_UNRESTRICTED_BASE_TYPE, use_unrestricted_base_type);
193 exec!(CONVERT_DYNAMIC_TO_CHOICE, convert_dynamic_to_choice);
194 exec!(FLATTEN_ELEMENT_CONTENT, flatten_element_content);
195 exec!(FLATTEN_UNIONS, flatten_unions);
196 exec!(MERGE_ENUM_UNIONS, merge_enum_unions);
197 exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
198 exec!(REMOVE_DUPLICATES, remove_duplicates);
199 exec!(RESOLVE_TYPEDEFS, resolve_typedefs);
200
201 let types = optimizer.finish();
202
203 if let Some(output) = config.debug_output {
204 let printer = TypesPrinter::new(&types);
205 let debug = format!("{printer}");
206
207 write(output, debug)?;
208 }
209
210 Ok(types)
211}
212
213#[instrument(err, level = "trace", skip(schemas, types))]
219pub fn exec_generator(
220 config: GeneratorConfig,
221 schemas: &Schemas,
222 types: &Types,
223) -> Result<TokenStream, Error> {
224 tracing::info!("Generate Code");
225
226 let mut generator = Generator::new(types)
227 .box_flags(config.box_flags)
228 .content_mode(config.content_mode)
229 .typedef_mode(config.typedef_mode)
230 .serde_support(config.serde_support)
231 .xsd_parser_crate(config.xsd_parser)
232 .generate_flags(config.flags);
233
234 if let Some(derive) = config.derive {
235 generator = generator.derive(derive);
236 }
237
238 if let Some(traits) = config.dyn_type_traits {
239 generator = generator.dyn_type_traits(traits);
240 }
241
242 generator = generator.with_type_postfix(IdentType::Type, config.type_postfix.type_);
243 generator = generator.with_type_postfix(IdentType::Element, config.type_postfix.element);
244 generator =
245 generator.with_type_postfix(IdentType::ElementType, config.type_postfix.element_type);
246
247 for (type_, ident) in config.types {
248 let ident = resolve_ident(schemas, &ident, type_)?;
249
250 generator = generator.with_type(ident)?;
251 }
252
253 match config.generate {
254 Generate::All => generator = generator.generate_all_types()?,
255 Generate::Types(idents) => {
256 for (type_, ident) in idents {
257 let ident = resolve_ident(schemas, &ident, type_)?;
258
259 generator = generator.generate_type(ident)?;
260 }
261 }
262 }
263
264 let code = generator.finish();
265
266 Ok(code)
267}
268
269fn resolve_ident(schemas: &Schemas, ident: &str, type_: IdentType) -> Result<Ident, Error> {
270 let (prefix, name) = ident
271 .split_once(':')
272 .map_or((None, ident), |(a, b)| (Some(a), b));
273 let ns = prefix
274 .map(|s| NamespacePrefix::new(s.as_bytes().to_owned()))
275 .map(|prefix| {
276 schemas
277 .resolve_prefix(&prefix)
278 .ok_or_else(|| InterpreterError::UnknownNamespacePrefix(prefix.clone()))
279 })
280 .transpose()?;
281
282 Ok(Ident {
283 name: Name::new(name),
284 ns,
285 type_,
286 })
287}