1use super::{GenEnum, GenStruct, GenerateMod, Impl, ImplFor, StreamBuilder, StringOrIdent};
2use crate::parse::{GenericConstraints, Generics};
3use crate::prelude::{Ident, TokenStream};
4
5#[must_use]
6pub struct Generator {
12 name: Ident,
13 generics: Option<Generics>,
14 generic_constraints: Option<GenericConstraints>,
15 stream: StreamBuilder,
16}
17
18impl Generator {
19 pub(crate) fn new(
20 name: Ident,
21 generics: Option<Generics>,
22 generic_constraints: Option<GenericConstraints>,
23 ) -> Self {
24 Self {
25 name,
26 generics,
27 generic_constraints,
28 stream: StreamBuilder::new(),
29 }
30 }
31
32 pub fn target_name(&self) -> Ident {
34 self.name.clone()
35 }
36
37 pub fn r#impl(&mut self) -> Impl<'_, Self> {
41 Impl::with_parent_name(self)
42 }
43
44 pub fn generate_impl(&mut self) -> Impl<'_, Self> {
50 Impl::with_parent_name(self)
51 }
52
53 pub fn impl_for(&mut self, trait_name: impl Into<String>) -> ImplFor<'_, Self> {
57 ImplFor::new(
58 self,
59 self.name.clone().into(),
60 Some(trait_name.into().into()),
61 )
62 }
63
64 pub fn impl_for_other_type(
75 &mut self,
76 type_name: impl Into<StringOrIdent>,
77 ) -> ImplFor<'_, Self> {
78 ImplFor::new(self, type_name.into(), None)
79 }
80
81 pub fn impl_trait_for_other_type(
92 &mut self,
93 trait_name: impl Into<StringOrIdent>,
94 type_name: impl Into<StringOrIdent>,
95 ) -> ImplFor<'_, Self> {
96 ImplFor::new(self, type_name.into(), Some(trait_name.into()))
97 }
98
99 pub fn impl_for_with_lifetimes<ITER, T>(
128 &mut self,
129 trait_name: T,
130 lifetimes: ITER,
131 ) -> ImplFor<'_, Self>
132 where
133 ITER: IntoIterator,
134 ITER::Item: Into<String>,
135 T: Into<StringOrIdent>,
136 {
137 ImplFor::new(self, self.name.clone().into(), Some(trait_name.into()))
138 .with_lifetimes(lifetimes)
139 }
140
141 pub fn generate_struct(&mut self, name: impl Into<String>) -> GenStruct<'_, Self> {
143 GenStruct::new(self, name)
144 }
145
146 pub fn generate_enum(&mut self, name: impl Into<String>) -> GenEnum<'_, Self> {
148 GenEnum::new(self, name)
149 }
150
151 pub fn generate_mod(&mut self, mod_name: impl Into<String>) -> GenerateMod<'_, Self> {
153 GenerateMod::new(self, mod_name)
154 }
155
156 pub fn export_to_file(&self, crate_name: &str, file_postfix: &str) -> bool {
163 use std::io::Write;
164
165 if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") {
166 let mut path = std::path::PathBuf::from(var);
167 loop {
168 {
169 let mut path = path.clone();
170 path.push("target");
171 if path.exists() {
172 path.push("generated");
173 path.push(crate_name);
174 if std::fs::create_dir_all(&path).is_err() {
175 return false;
176 }
177 path.push(format!("{}_{}.rs", self.target_name(), file_postfix));
178 let result = std::fs::File::create(path);
179 if let Ok(mut file) = result {
180 let _ = file.write_all(self.stream.stream.to_string().as_bytes());
181 return true;
182 }
183 }
184 }
185 if let Some(parent) = path.parent() {
186 path = parent.into();
187 } else {
188 break;
189 }
190 }
191 }
192 false
193 }
194
195 pub fn finish(mut self) -> crate::prelude::Result<TokenStream> {
197 Ok(std::mem::take(&mut self.stream).stream)
198 }
199}
200
201#[cfg(feature = "proc-macro2")]
202impl Generator {
203 pub fn with_name(name: &str) -> Self {
205 Self::new(
206 Ident::new(name, crate::prelude::Span::call_site()),
207 None,
208 None,
209 )
210 }
211 pub fn with_lifetime(mut self, lt: &str) -> Self {
213 self.generics
214 .get_or_insert_with(|| Generics(Vec::new()))
215 .push(crate::parse::Generic::Lifetime(crate::parse::Lifetime {
216 ident: crate::prelude::Ident::new(lt, crate::prelude::Span::call_site()),
217 constraint: Vec::new(),
218 }));
219 self
220 }
221 pub fn assert_eq(&self, expected: &str) {
223 assert_eq!(expected, self.stream.stream.to_string());
224 }
225}
226
227impl Drop for Generator {
228 fn drop(&mut self) {
229 if !self.stream.stream.is_empty() && !std::thread::panicking() {
230 eprintln!(
231 "WARNING: Generator dropped but the stream is not empty. Please call `.finish()` on the generator"
232 );
233 }
234 }
235}
236
237impl super::Parent for Generator {
238 fn append(&mut self, builder: StreamBuilder) {
239 self.stream.append(builder);
240 }
241
242 fn name(&self) -> &Ident {
243 &self.name
244 }
245
246 fn generics(&self) -> Option<&Generics> {
247 self.generics.as_ref()
248 }
249
250 fn generic_constraints(&self) -> Option<&GenericConstraints> {
251 self.generic_constraints.as_ref()
252 }
253}
254
255#[cfg(test)]
256mod test {
257 use proc_macro2::Span;
258
259 use crate::token_stream;
260
261 use super::*;
262
263 #[test]
264 fn impl_for_with_lifetimes() {
265 let mut generator =
267 Generator::new(Ident::new("StructOrEnum", Span::call_site()), None, None);
268 let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]);
269 let output = generator.finish().unwrap();
270 assert_eq!(
271 output
272 .into_iter()
273 .map(|v| v.to_string())
274 .collect::<String>(),
275 token_stream("impl<'a, 'b> Foo<'a, 'b> for StructOrEnum { }")
276 .map(|v| v.to_string())
277 .collect::<String>(),
278 );
279
280 let mut generator = Generator::new(
282 Ident::new("StructOrEnum", Span::call_site()),
283 Generics::try_take(&mut token_stream("<T1, T2>")).unwrap(),
284 None,
285 );
286 let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]);
287 let output = generator.finish().unwrap();
288 assert_eq!(
289 output
290 .into_iter()
291 .map(|v| v.to_string())
292 .collect::<String>(),
293 token_stream("impl<'a, 'b, T1, T2> Foo<'a, 'b> for StructOrEnum<T1, T2> { }")
294 .map(|v| v.to_string())
295 .collect::<String>()
296 );
297
298 let mut generator = Generator::new(
300 Ident::new("StructOrEnum", Span::call_site()),
301 Generics::try_take(&mut token_stream("<'alpha, 'beta>")).unwrap(),
302 None,
303 );
304 let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]);
305 let output = generator.finish().unwrap();
306 assert_eq!(
307 output
308 .into_iter()
309 .map(|v| v.to_string())
310 .collect::<String>(),
311 token_stream(
312 "impl<'a, 'b, 'alpha, 'beta> Foo<'a, 'b> for StructOrEnum<'alpha, 'beta> { }"
313 )
314 .map(|v| v.to_string())
315 .collect::<String>()
316 );
317 }
318
319 #[test]
320 fn impl_for_with_trait_generics() {
321 let mut generator = Generator::new(
322 Ident::new("StructOrEnum", Span::call_site()),
323 Generics::try_take(&mut token_stream("<'a>")).unwrap(),
324 None,
325 );
326 let _ = generator.impl_for("Foo").with_trait_generics(["&'a str"]);
327 let output = generator.finish().unwrap();
328 assert_eq!(
329 output
330 .into_iter()
331 .map(|v| v.to_string())
332 .collect::<String>(),
333 token_stream("impl<'a> Foo<&'a str> for StructOrEnum<'a> { }")
334 .map(|v| v.to_string())
335 .collect::<String>(),
336 );
337 }
338
339 #[test]
340 fn impl_for_with_impl_generics() {
341 let mut generator = Generator::new(
343 Ident::new("StructOrEnum", Span::call_site()),
344 Generics::try_take(&mut token_stream("<T1, T2>")).unwrap(),
345 None,
346 );
347 let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]);
348
349 let output = generator.finish().unwrap();
350 assert_eq!(
351 output
352 .into_iter()
353 .map(|v| v.to_string())
354 .collect::<String>(),
355 token_stream("impl<T1, T2, Bar> Foo for StructOrEnum<T1, T2> { }")
356 .map(|v| v.to_string())
357 .collect::<String>()
358 );
359 let mut generator = Generator::new(
361 Ident::new("StructOrEnum", Span::call_site()),
362 Generics::try_take(&mut token_stream("<'alpha, 'beta>")).unwrap(),
363 None,
364 );
365 let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]);
366 let output = generator.finish().unwrap();
367 assert_eq!(
368 output
369 .into_iter()
370 .map(|v| v.to_string())
371 .collect::<String>(),
372 token_stream("impl<'alpha, 'beta, Bar> Foo for StructOrEnum<'alpha, 'beta> { }")
373 .map(|v| v.to_string())
374 .collect::<String>()
375 );
376 }
377}