xsd_parser/models/code/
module.rs1use std::collections::BTreeMap;
2use std::fs::{create_dir_all, write};
3use std::io::Error as IoError;
4use std::mem::replace;
5use std::path::Path;
6use std::str::FromStr;
7
8use proc_macro2::{Ident as Ident2, TokenStream};
9use quote::{format_ident, quote, ToTokens};
10
11use super::IdentPath;
12
13#[derive(Default, Debug)]
18pub struct Module {
19 pub code: TokenStream,
21
22 pub usings: BTreeMap<String, bool>,
24
25 pub modules: BTreeMap<String, Module>,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum SubModules {
33 None,
35
36 Files,
47
48 Inline,
64}
65
66impl Module {
67 pub fn append(&mut self, code: TokenStream) -> &mut Self {
69 self.code.extend(code);
70
71 self
72 }
73
74 pub fn prepend(&mut self, code: TokenStream) -> &mut Self {
76 let code = replace(&mut self.code, code);
77
78 self.append(code)
79 }
80
81 pub fn usings<I>(&mut self, anonymous: bool, usings: I) -> &mut Self
83 where
84 I: IntoIterator,
85 I::Item: ToString,
86 {
87 for using in usings {
88 *self.usings.entry(using.to_string()).or_insert(anonymous) &= anonymous;
89 }
90
91 self
92 }
93
94 pub fn module<T>(&self, ident: T) -> Option<&Module>
98 where
99 T: AsRef<str>,
100 {
101 self.modules.get(ident.as_ref())
102 }
103
104 pub fn module_mut<T>(&mut self, ident: T) -> &mut Module
108 where
109 T: Into<String>,
110 {
111 self.modules.entry(ident.into()).or_default()
112 }
113
114 pub fn to_code(&self, tokens: &mut TokenStream, sub_modules: SubModules) {
120 let Self {
121 code,
122 usings,
123 modules,
124 } = self;
125
126 if sub_modules == SubModules::Files {
127 let modules = modules.iter().map(|(ident, _module)| {
128 let name = format_ident!("{ident}");
129
130 quote!(pub mod #name;)
131 });
132
133 tokens.extend(quote! {
134 #( #modules )*
135 });
136 }
137
138 let usings = render_usings(usings.iter());
139
140 tokens.extend(quote! {
141 #usings
142 #code
143 });
144
145 if sub_modules == SubModules::Inline {
146 for (ident, module) in modules {
147 let name = format_ident!("{ident}");
148
149 tokens.extend(quote! {
150 pub mod #name {
151 #module
152 }
153 });
154 }
155 }
156 }
157
158 pub fn write_to_files<P>(&self, directory: P) -> Result<(), IoError>
167 where
168 P: AsRef<Path>,
169 {
170 let directory = directory.as_ref();
171
172 self.write_to_files_with(|module: &Module, path: &Path| {
173 let filename = if module.modules.is_empty() {
174 directory.join(path).with_extension("rs")
175 } else {
176 directory.join(path).join("mod.rs")
177 };
178 create_dir_all(filename.parent().unwrap())?;
179
180 let mut code = TokenStream::new();
181 module.to_code(&mut code, SubModules::Files);
182
183 let code = code.to_string();
184 write(filename, code)?;
185
186 Ok(())
187 })
188 }
189
190 pub fn write_to_files_with<F, E>(&self, mut f: F) -> Result<(), E>
199 where
200 F: FnMut(&Module, &Path) -> Result<(), E>,
201 {
202 self.write_to_files_with_impl("", &mut f)
203 }
204
205 fn write_to_files_with_impl<P, F, E>(&self, path: P, f: &mut F) -> Result<(), E>
206 where
207 P: AsRef<Path>,
208 F: FnMut(&Module, &Path) -> Result<(), E>,
209 {
210 let path = path.as_ref();
211
212 f(self, path)?;
213
214 for (ident, module) in &self.modules {
215 let path = path.join(ident);
216
217 module.write_to_files_with_impl(path, f)?;
218 }
219
220 Ok(())
221 }
222}
223
224impl ToTokens for Module {
225 fn to_tokens(&self, tokens: &mut TokenStream) {
226 self.to_code(tokens, SubModules::Inline);
227 }
228}
229
230fn render_usings<'x, I, X>(usings: I) -> TokenStream
231where
232 I: IntoIterator<Item = (X, &'x bool)>,
233 X: AsRef<str>,
234{
235 #[derive(Default)]
236 struct Module {
237 usings: BTreeMap<Ident2, bool>,
238 sub_modules: BTreeMap<Ident2, Module>,
239 }
240
241 impl Module {
242 fn render(&self) -> TokenStream {
243 let count = self.usings.len() + self.sub_modules.len();
244
245 let usings = self.usings.iter().map(|(ident, anonymous)| {
246 if *anonymous {
247 quote!(#ident as _)
248 } else {
249 quote!(#ident)
250 }
251 });
252 let sub_modules = self.sub_modules.iter().map(|(ident, module)| {
253 let using = module.render();
254
255 quote!(#ident::#using)
256 });
257
258 let items = usings.chain(sub_modules);
259
260 if count > 1 {
261 quote!({ #( #items ),* })
262 } else {
263 quote!(#( #items )*)
264 }
265 }
266 }
267
268 let mut root = Module::default();
269
270 for (using, anonymous) in usings {
271 let using = using.as_ref();
272 let Ok(ident) = IdentPath::from_str(using) else {
273 continue;
274 };
275
276 let (ident, path, _) = ident.into_parts();
277
278 let mut module = &mut root;
279 for part in path.into_iter().flat_map(|x| x.0) {
280 module = module.sub_modules.entry(part).or_default();
281 }
282
283 *module.usings.entry(ident).or_insert(*anonymous) &= *anonymous;
284 }
285
286 let mut ret = TokenStream::new();
287 for (ident, module) in &root.sub_modules {
288 let using = module.render();
289 ret.extend(quote!(use #ident::#using;));
290 }
291
292 ret
293}