virtue/generate/
impl.rs

1use super::{generate_item::FnParent, FnBuilder, GenConst, Generator, Parent, StreamBuilder};
2use crate::{
3    parse::{GenericConstraints, Generics},
4    prelude::{Delimiter, Result},
5};
6
7#[must_use]
8/// A helper struct for implementing functions for a given struct or enum.
9pub struct Impl<'a, P: Parent> {
10    parent: &'a mut P,
11    outer_attr: Vec<StreamBuilder>,
12    inner_attr: Vec<StreamBuilder>,
13    name: String,
14    // pub(super) group: StreamBuilder,
15    consts: Vec<StreamBuilder>,
16    custom_generic_constraints: Option<GenericConstraints>,
17    fns: Vec<(StreamBuilder, StreamBuilder)>,
18}
19
20impl<'a, P: Parent> Impl<'a, P> {
21    pub(super) fn with_parent_name(parent: &'a mut P) -> Self {
22        Self {
23            outer_attr: Vec::new(),
24            inner_attr: Vec::new(),
25            name: parent.name().to_string(),
26            parent,
27            consts: Vec::new(),
28            custom_generic_constraints: None,
29            fns: Vec::new(),
30        }
31    }
32
33    pub(super) fn new(parent: &'a mut P, name: impl Into<String>) -> Self {
34        Self {
35            outer_attr: Vec::new(),
36            inner_attr: Vec::new(),
37            parent,
38            name: name.into(),
39            consts: Vec::new(),
40            custom_generic_constraints: None,
41            fns: Vec::new(),
42        }
43    }
44
45    /// Add a outer attribute to the trait implementation
46    pub fn impl_outer_attr(&mut self, attr: impl AsRef<str>) -> Result {
47        let mut builder = StreamBuilder::new();
48        builder.punct('#').group(Delimiter::Bracket, |builder| {
49            builder.push_parsed(attr)?;
50            Ok(())
51        })?;
52        self.outer_attr.push(builder);
53        Ok(())
54    }
55
56    /// Add a inner attribute to the trait implementation
57    pub fn impl_inner_attr(&mut self, attr: impl AsRef<str>) -> Result {
58        let mut builder = StreamBuilder::new();
59        builder
60            .punct('#')
61            .punct('!')
62            .group(Delimiter::Brace, |builder| {
63                builder.push_parsed(attr)?;
64                Ok(())
65            })?;
66        self.inner_attr.push(builder);
67        Ok(())
68    }
69
70    /// Add a function to the trait implementation.
71    ///
72    /// `generator.impl().generate_fn("bar")` results in code like:
73    ///
74    /// ```ignore
75    /// impl <struct or enum> {
76    ///     fn bar() {}
77    /// }
78    /// ```
79    ///
80    /// See [`FnBuilder`] for more options, as well as information on how to fill the function body.
81    pub fn generate_fn(&mut self, name: impl Into<String>) -> FnBuilder<Self> {
82        FnBuilder::new(self, name)
83    }
84
85    /// Add a const to the trait implementation
86    /// ```
87    /// # use virtue::prelude::Generator;
88    /// # let mut generator = Generator::with_name("Bar");
89    /// generator.impl_for("Foo")
90    ///          .generate_const("BAR", "u8")
91    ///          .with_value(|b| {
92    ///             b.push_parsed("5")?;
93    ///             Ok(())
94    ///          })?;
95    /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }");
96    /// # Ok::<_, virtue::Error>(())
97    /// ```
98    ///
99    /// Generates:
100    /// ```ignore
101    /// impl Foo for <struct or enum> {
102    ///     const BAR: u8 = 5;
103    /// }
104    pub fn generate_const(&mut self, name: impl Into<String>, ty: impl Into<String>) -> GenConst {
105        GenConst::new(&mut self.consts, name, ty)
106    }
107}
108
109impl<'a> Impl<'a, Generator> {
110    /// Modify the generic constraints of a type.
111    /// This can be used to add additional type constraints to your implementation.
112    ///
113    /// ```ignore
114    /// // Your derive:
115    /// #[derive(YourTrait)]
116    /// pub struct Foo<B> {
117    ///     ...
118    /// }
119    ///
120    /// // With this code:
121    /// generator
122    ///     .r#impl()
123    ///     .modify_generic_constraints(|generics, constraints| {
124    ///         for g in generics.iter_generics() {
125    ///             constraints.push_generic(g, "YourTrait");
126    ///         }
127    ///     })
128    ///
129    /// // will generate:
130    /// impl<B> Foo<B>
131    ///     where B: YourTrait // <-
132    /// {
133    /// }
134    /// ```
135    ///
136    /// Note that this function is only implemented when you call `.r#impl` on [`Generator`].
137    pub fn modify_generic_constraints<CB>(&mut self, cb: CB) -> &mut Self
138    where
139        CB: FnOnce(&Generics, &mut GenericConstraints),
140    {
141        if let Some(generics) = self.parent.generics() {
142            let constraints = self.custom_generic_constraints.get_or_insert_with(|| {
143                self.parent
144                    .generic_constraints()
145                    .cloned()
146                    .unwrap_or_default()
147            });
148            cb(generics, constraints);
149        }
150        self
151    }
152}
153
154impl<'a, P: Parent> FnParent for Impl<'a, P> {
155    fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result {
156        self.fns.push((fn_definition, fn_body));
157        Ok(())
158    }
159}
160
161impl<'a, P: Parent> Drop for Impl<'a, P> {
162    fn drop(&mut self) {
163        if std::thread::panicking() {
164            return;
165        }
166        let mut builder = StreamBuilder::new();
167        for attr in std::mem::take(&mut self.outer_attr) {
168            builder.append(attr);
169        }
170        builder.ident_str("impl");
171
172        if let Some(generics) = self.parent.generics() {
173            builder.append(generics.impl_generics());
174        }
175        builder.push_parsed(&self.name).unwrap();
176
177        if let Some(generics) = self.parent.generics() {
178            builder.append(generics.type_generics());
179        }
180        if let Some(generic_constraints) = self.custom_generic_constraints.take() {
181            builder.append(generic_constraints.where_clause());
182        } else if let Some(generic_constraints) = self.parent.generic_constraints() {
183            builder.append(generic_constraints.where_clause());
184        }
185
186        builder
187            .group(Delimiter::Brace, |builder| {
188                for attr in std::mem::take(&mut self.inner_attr) {
189                    builder.append(attr);
190                }
191                for r#const in std::mem::take(&mut self.consts) {
192                    builder.append(r#const);
193                }
194                for (fn_def, fn_body) in std::mem::take(&mut self.fns) {
195                    builder.append(fn_def);
196                    builder
197                        .group(Delimiter::Brace, |body| {
198                            *body = fn_body;
199                            Ok(())
200                        })
201                        .unwrap();
202                }
203                Ok(())
204            })
205            .unwrap();
206
207        self.parent.append(builder);
208    }
209}