virtue/generate/
impl_for.rs

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