Skip to main content

virtue_next/generate/
impl.rs

1use super::{FnBuilder, GenConst, Generator, Parent, StreamBuilder, generate_item::FnParent};
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(
105        &mut self,
106        name: impl Into<String>,
107        ty: impl Into<String>,
108    ) -> GenConst<'_> {
109        GenConst::new(&mut self.consts, name, ty)
110    }
111}
112
113impl<'a> Impl<'a, Generator> {
114    /// Modify the generic constraints of a type.
115    /// This can be used to add additional type constraints to your implementation.
116    ///
117    /// ```ignore
118    /// // Your derive:
119    /// #[derive(YourTrait)]
120    /// pub struct Foo<B> {
121    ///     ...
122    /// }
123    ///
124    /// // With this code:
125    /// generator
126    ///     .r#impl()
127    ///     .modify_generic_constraints(|generics, constraints| {
128    ///         for g in generics.iter_generics() {
129    ///             constraints.push_generic(g, "YourTrait");
130    ///         }
131    ///     })
132    ///
133    /// // will generate:
134    /// impl<B> Foo<B>
135    ///     where B: YourTrait // <-
136    /// {
137    /// }
138    /// ```
139    ///
140    /// Note that this function is only implemented when you call `.r#impl` on [`Generator`].
141    pub fn modify_generic_constraints<CB>(&mut self, cb: CB) -> &mut Self
142    where
143        CB: FnOnce(&Generics, &mut GenericConstraints),
144    {
145        if let Some(generics) = self.parent.generics() {
146            let constraints = self.custom_generic_constraints.get_or_insert_with(|| {
147                self.parent
148                    .generic_constraints()
149                    .cloned()
150                    .unwrap_or_default()
151            });
152            cb(generics, constraints);
153        }
154        self
155    }
156}
157
158impl<'a, P: Parent> FnParent for Impl<'a, P> {
159    fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result {
160        self.fns.push((fn_definition, fn_body));
161        Ok(())
162    }
163}
164
165impl<'a, P: Parent> Drop for Impl<'a, P> {
166    fn drop(&mut self) {
167        if std::thread::panicking() {
168            return;
169        }
170        let mut builder = StreamBuilder::new();
171        for attr in std::mem::take(&mut self.outer_attr) {
172            builder.append(attr);
173        }
174        builder.ident_str("impl");
175
176        if let Some(generics) = self.parent.generics() {
177            builder.append(generics.impl_generics());
178        }
179        builder.push_parsed(&self.name).unwrap();
180
181        if let Some(generics) = self.parent.generics() {
182            builder.append(generics.type_generics());
183        }
184        match self.custom_generic_constraints.take() {
185            Some(generic_constraints) => {
186                builder.append(generic_constraints.where_clause());
187            }
188            _ => {
189                if let Some(generic_constraints) = self.parent.generic_constraints() {
190                    builder.append(generic_constraints.where_clause());
191                }
192            }
193        }
194
195        builder
196            .group(Delimiter::Brace, |builder| {
197                for attr in std::mem::take(&mut self.inner_attr) {
198                    builder.append(attr);
199                }
200                for r#const in std::mem::take(&mut self.consts) {
201                    builder.append(r#const);
202                }
203                for (fn_def, fn_body) in std::mem::take(&mut self.fns) {
204                    builder.append(fn_def);
205                    builder
206                        .group(Delimiter::Brace, |body| {
207                            *body = fn_body;
208                            Ok(())
209                        })
210                        .unwrap();
211                }
212                Ok(())
213            })
214            .unwrap();
215
216        self.parent.append(builder);
217    }
218}