Skip to main content

virtue_next/generate/
impl_for.rs

1use super::FnBuilder;
2use super::GenConst;
3use super::Parent;
4use super::StreamBuilder;
5use super::StringOrIdent;
6use super::generate_item::FnParent;
7use crate::parse::GenericConstraints;
8use crate::parse::Generics;
9use crate::prelude::Delimiter;
10use crate::prelude::Result;
11
12#[must_use]
13/// A helper struct for implementing a trait for a given struct or enum.
14pub struct ImplFor<'a, P: Parent> {
15    generator: &'a mut P,
16    outer_attr: Vec<StreamBuilder>,
17    inner_attr: Vec<StreamBuilder>,
18    type_name: StringOrIdent,
19    trait_name: Option<StringOrIdent>,
20    lifetimes: Option<Vec<String>>,
21    trait_generics: Option<Vec<String>>,
22    impl_generics: Vec<String>,
23    consts: Vec<StreamBuilder>,
24    custom_generic_constraints: Option<GenericConstraints>,
25    impl_types: Vec<StreamBuilder>,
26    fns: Vec<(StreamBuilder, StreamBuilder)>,
27    is_unsafe: bool,
28}
29
30impl<'a, P: Parent> ImplFor<'a, P> {
31    pub(super) const fn new(
32        generator: &'a mut P,
33        type_name: StringOrIdent,
34        trait_name: Option<StringOrIdent>,
35    ) -> Self {
36        Self {
37            generator,
38            outer_attr: Vec::new(),
39            inner_attr: Vec::new(),
40            trait_name,
41            type_name,
42            lifetimes: None,
43            trait_generics: None,
44            impl_generics: vec![],
45            consts: Vec::new(),
46            custom_generic_constraints: None,
47            impl_types: Vec::new(),
48            fns: Vec::new(),
49            is_unsafe: false,
50        }
51    }
52
53    /// Internal helper function to set lifetimes
54    pub(crate) fn with_lifetimes<ITER>(
55        mut self,
56        lifetimes: ITER,
57    ) -> Self
58    where
59        ITER: IntoIterator,
60        ITER::Item: Into<String>,
61    {
62        self.lifetimes = Some(lifetimes.into_iter().map(Into::into).collect());
63        self
64    }
65
66    /// Make the new lifetimes added by `Generator::impl_for_with_lifetimes` depend on the existing lifetimes from the original derive.
67    ///
68    /// See [`impl_for_with_lifetimes`] for more information.
69    ///
70    /// Calling this method in any other context has no effect.
71    ///
72    /// [`impl_for_with_lifetimes`]: struct.Generator.html#method.impl_for_with_lifetimes
73    ///
74    /// # Panics
75    ///
76    /// Panics if an internal invariant is violated.
77    pub fn new_lifetimes_depend_on_existing(mut self) -> Self {
78        if let Some(new_lt) = &self.lifetimes
79            && let Some(generics) = self.generator.generics()
80        {
81            let constraints = self.custom_generic_constraints.get_or_insert_with(|| {
82                self.generator
83                    .generic_constraints()
84                    .cloned()
85                    .unwrap_or_default()
86            });
87            for old_lt in generics.iter_lifetimes() {
88                for new_lt in new_lt {
89                    constraints
90                        .push_parsed_constraint(format!("'{}: '{}", new_lt, old_lt.ident))
91                        .expect("Could not ensure new lifetimes depend on existing lifetimes");
92                }
93            }
94        }
95        self
96    }
97
98    /// Add unsafe generic parameters to the trait implementation.
99    /// ```
100    /// Make the generated `impl` block unsafe.
101    ///
102    /// Generates:
103    /// ```ignore
104    /// unsafe impl Foo for Bar { }
105    /// ```
106    pub const fn make_unsafe(mut self) -> Self {
107        self.is_unsafe = true;
108        self
109    }
110
111    /// Add generic parameters to the trait implementation.
112    /// ```
113    /// # use virtue::prelude::Generator;
114    /// # let mut generator = Generator::with_name("Bar");
115    /// generator.impl_for("Foo").with_trait_generics(["Baz"]);
116    /// # generator.assert_eq("impl Foo < Baz > for Bar { }");
117    /// # Ok::<_, virtue::Error>(())
118    /// ```
119    ///
120    /// Generates:
121    /// ```ignore
122    /// impl Foo for <struct or enum> {
123    ///     const BAR: u8 = 5;
124    /// }
125    pub fn with_trait_generics<ITER>(
126        mut self,
127        generics: ITER,
128    ) -> Self
129    where
130        ITER: IntoIterator,
131        ITER::Item: Into<String>,
132    {
133        self.trait_generics = Some(generics.into_iter().map(Into::into).collect());
134        self
135    }
136
137    /// # Errors
138    ///
139    /// Returns an error if the operation fails.
140
141    /// Add generic parameters to the impl block.
142    /// ```
143    /// # use virtue::prelude::Generator;
144    /// # let mut generator = Generator::with_name("Bar");
145    /// generator.impl_for("Foo").with_impl_generics(["Baz"]);
146    /// # generator.assert_eq("impl < Baz > Foo for Bar { }");
147    /// # Ok::<_, virtue::Error>(())
148    /// ```
149    ///
150    /// Generates:
151    /// ```ignore
152    /// impl<Baz> Foo for Bar { }
153    /// ```
154    pub fn with_impl_generics<ITER>(
155        mut self,
156        generics: ITER,
157    ) -> Self
158    where
159        ITER: IntoIterator,
160        ITER::Item: Into<String>,
161    {
162        self.impl_generics = generics.into_iter().map(Into::into).collect();
163        self
164    }
165
166    /// Add a outer attribute to the trait implementation
167    ///
168    /// # Errors
169    ///
170    /// Returns an error if parsing fails.
171    pub fn impl_outer_attr(
172        &mut self,
173        attr: impl AsRef<str>,
174    ) -> Result {
175        let mut builder = StreamBuilder::new();
176        builder.punct('#').group(Delimiter::Bracket, |builder| {
177            builder.push_parsed(attr)?;
178            Ok(())
179        })?;
180        self.outer_attr.push(builder);
181        Ok(())
182    }
183
184    /// Add a inner attribute to the trait implementation
185    ///
186    /// # Errors
187    ///
188    /// Returns an error if parsing fails.
189    pub fn impl_inner_attr(
190        &mut self,
191        attr: impl AsRef<str>,
192    ) -> Result {
193        let mut builder = StreamBuilder::new();
194        builder
195            .punct('#')
196            .punct('!')
197            .group(Delimiter::Brace, |builder| {
198                builder.push_parsed(attr)?;
199                Ok(())
200            })?;
201        self.inner_attr.push(builder);
202        Ok(())
203    }
204
205    /// Add a const to the trait implementation
206    /// ```
207    /// # use virtue::prelude::Generator;
208    /// # let mut generator = Generator::with_name("Bar");
209    /// generator
210    ///     .impl_for("Foo")
211    ///     .generate_const("BAR", "u8")
212    ///     .with_value(|b| {
213    ///         b.push_parsed("5")?;
214    ///         Ok(())
215    ///     })?;
216    /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }");
217    /// # Ok::<_, virtue::Error>(())
218    /// ```
219    ///
220    /// Generates:
221    /// ```ignore
222    /// impl Foo for <struct or enum> {
223    ///     const BAR: u8 = 5;
224    /// }
225    pub fn generate_const(
226        &mut self,
227        name: impl Into<String>,
228        ty: impl Into<String>,
229    ) -> GenConst<'_> {
230        GenConst::new(&mut self.consts, name, ty)
231    }
232
233    /// # Errors
234    ///
235    /// Returns an error if the operation fails.
236
237    /// Add a function to the trait implementation.
238    ///
239    /// `generator.impl_for("Foo").generate_fn("bar")` results in code like:
240    ///
241    /// ```ignore
242    /// impl Foo for <struct or enum> {
243    ///     fn bar() {}
244    /// }
245    /// ```
246    ///
247    /// See [`FnBuilder`] for more options, as well as information on how to fill the function body.
248    pub fn generate_fn(
249        &mut self,
250        name: impl Into<String>,
251    ) -> FnBuilder<'_, Self> {
252        FnBuilder::new(self, name)
253    }
254
255    /// Add a type to the impl
256    ///
257    /// `generator.impl_for("Foo").impl_type("Bar", "u8")` results in code like:
258    ///
259    /// ```ignore
260    /// impl Foo for <struct or enum> {
261    ///     type Bar = u8;
262    /// }
263    /// ```
264    ///
265    /// # Errors
266    ///
267    /// Returns an error if parsing fails.
268    pub fn impl_type(
269        &mut self,
270        name: impl AsRef<str>,
271        value: impl AsRef<str>,
272    ) -> Result {
273        let mut builder = StreamBuilder::new();
274        builder
275            .ident_str("type")
276            .push_parsed(name)?
277            .punct('=')
278            .push_parsed(value)?
279            .punct(';');
280        self.impl_types.push(builder);
281        Ok(())
282    }
283
284    /// Modify the generic constraints of a type.
285    /// This can be used to add additional type constraints to your implementation.
286    ///
287    /// ```ignore
288    /// // Your derive:
289    /// #[derive(YourTrait)]
290    /// pub struct Foo<B> {
291    ///     ...
292    /// }
293    ///
294    /// // With this code:
295    /// generator
296    ///     .impl_for("YourTrait")
297    ///     .modify_generic_constraints(|generics, constraints| {
298    ///         for g in generics.iter_generics() {
299    ///             constraints.push_generic(g, "YourTrait");
300    ///         }
301    ///     })
302    ///
303    /// // will generate:
304    /// impl<B> YourTrait for Foo<B>
305    ///     where B: YourTrait // <-
306    /// {
307    /// }
308    /// ```
309    ///
310    /// # Errors
311    ///
312    /// Returns an error if parsing fails.
313    pub fn modify_generic_constraints<CB>(
314        &mut self,
315        cb: CB,
316    ) -> Result<&mut Self>
317    where
318        CB: FnOnce(&Generics, &mut GenericConstraints) -> Result,
319    {
320        if let Some(generics) = self.generator.generics() {
321            let constraints = self.custom_generic_constraints.get_or_insert_with(|| {
322                self.generator
323                    .generic_constraints()
324                    .cloned()
325                    .unwrap_or_default()
326            });
327            cb(generics, constraints)?;
328        }
329        Ok(self)
330    }
331}
332
333impl<P: Parent> FnParent for ImplFor<'_, P> {
334    fn append(
335        &mut self,
336        fn_definition: StreamBuilder,
337        fn_body: StreamBuilder,
338    ) -> Result {
339        self.fns.push((fn_definition, fn_body));
340        Ok(())
341    }
342}
343
344impl<P: Parent> Drop for ImplFor<'_, P> {
345    fn drop(&mut self) {
346        if std::thread::panicking() {
347            return;
348        }
349        let mut builder = StreamBuilder::new();
350        for attr in std::mem::take(&mut self.outer_attr) {
351            builder.append(attr);
352        }
353
354        self.generate_impl_definition(&mut builder);
355
356        builder
357            .group(Delimiter::Brace, |builder| {
358                for attr in std::mem::take(&mut self.inner_attr) {
359                    builder.append(attr);
360                }
361                for ty in std::mem::take(&mut self.impl_types) {
362                    builder.append(ty);
363                }
364                for r#const in std::mem::take(&mut self.consts) {
365                    builder.append(r#const);
366                }
367                for (fn_def, fn_body) in std::mem::take(&mut self.fns) {
368                    builder.append(fn_def);
369                    builder
370                        .group(Delimiter::Brace, |body| {
371                            *body = fn_body;
372                            Ok(())
373                        })
374                        .unwrap();
375                }
376                Ok(())
377            })
378            .unwrap();
379
380        self.generator.append(builder);
381    }
382}
383
384impl<P: Parent> ImplFor<'_, P> {
385    fn generate_impl_definition(
386        &mut self,
387        builder: &mut StreamBuilder,
388    ) {
389        if self.is_unsafe {
390            builder.ident_str("unsafe");
391        }
392        builder.ident_str("impl");
393
394        let impl_generics = self.impl_generics.as_slice();
395        if let Some(lifetimes) = &self.lifetimes {
396            if let Some(generics) = self.generator.generics() {
397                builder.append(generics.impl_generics_with_additional(lifetimes, impl_generics));
398            } else {
399                append_lifetimes_and_generics(builder, lifetimes, impl_generics);
400            }
401        } else if let Some(generics) = self.generator.generics() {
402            builder.append(generics.impl_generics_with_additional(&[], impl_generics));
403        } else if !impl_generics.is_empty() {
404            append_lifetimes_and_generics(builder, &[], impl_generics);
405        }
406        if let Some(t) = &self.trait_name {
407            builder.push_parsed(t.to_string()).unwrap();
408
409            let lifetimes = self.lifetimes.as_deref().unwrap_or_default();
410            let generics = self.trait_generics.as_deref().unwrap_or_default();
411            append_lifetimes_and_generics(builder, lifetimes, generics);
412            builder.ident_str("for");
413        }
414        builder.push_parsed(self.type_name.to_string()).unwrap();
415        if let Some(generics) = &self.generator.generics() {
416            builder.append(generics.type_generics());
417        }
418        match self.custom_generic_constraints.take() {
419            | Some(generic_constraints) => {
420                builder.append(generic_constraints.where_clause());
421            },
422            | _ => {
423                if let Some(generic_constraints) = &self.generator.generic_constraints() {
424                    builder.append(generic_constraints.where_clause());
425                }
426            },
427        }
428    }
429}
430
431fn append_lifetimes_and_generics(
432    builder: &mut StreamBuilder,
433    lifetimes: &[String],
434    generics: &[String],
435) {
436    if lifetimes.is_empty() && generics.is_empty() {
437        return;
438    }
439
440    builder.punct('<');
441
442    for (idx, lt) in lifetimes.iter().enumerate() {
443        if idx > 0 {
444            builder.punct(',');
445        }
446        builder.lifetime_str(lt);
447    }
448
449    for (idx, r#gen) in generics.iter().enumerate() {
450        if idx > 0 || !lifetimes.is_empty() {
451            builder.punct(',');
452        }
453        builder.push_parsed(r#gen).unwrap();
454    }
455
456    builder.punct('>');
457}