proffer/
function_gen.rs

1//!
2//!
3//! Function pieces, specifically `Function` which is composed of `FunctionSignature`
4//! and `FunctionBody`. Naturally, a `Function` can be used as a "method" for another
5//! object, by specifying `self` (or some variant of it) as the first `Parameter` to
6//! a `Function` object.
7//!
8
9use serde::Serialize;
10use tera::{Context, Tera};
11
12use crate::traits::SrcCode;
13use crate::{Generic, Generics};
14
15/// Represents a function or method. Determined if any `Parameter` contains `self`
16#[derive(Default, Serialize, Clone)]
17pub struct Function {
18    signature: FunctionSignature,
19    body: FunctionBody,
20}
21
22/// Represents a function/method signature in source code
23#[derive(Default, Serialize, Clone)]
24pub struct FunctionSignature {
25    name: String,
26    is_pub: bool,
27    is_async: bool,
28    parameters: Vec<Parameter>,
29    generics: Generics,
30    return_ty: Option<String>,
31}
32
33impl FunctionSignature {
34    /// Create a new function signature.
35    pub fn new<S: ToString>(name: S) -> Self {
36        let mut f = Self::default();
37        f.name = name.to_string();
38        f
39    }
40
41    /// Set this function as `async`
42    pub fn set_is_async(&mut self, is_async: bool) -> &mut Self {
43        self.is_async = is_async;
44        self
45    }
46
47    /// Add a parameter to this signature
48    pub fn add_parameter(&mut self, param: Parameter) -> &mut Self {
49        self.parameters.push(param);
50        self
51    }
52
53    /// Add a generic to this signature
54    pub fn add_generic(&mut self, generic: Generic) -> &mut Self {
55        self.generics.add_generic(generic);
56        self
57    }
58
59    /// Set a return type, if `None` will result in `()` type.
60    pub fn set_return_ty<S: ToString>(&mut self, ty: Option<S>) -> &mut Self {
61        self.return_ty = ty.map(|s| s.to_string());
62        self
63    }
64
65    /// Set if this signature should be prefixed with `pub`
66    pub fn set_is_pub(&mut self, is_pub: bool) -> &mut Self {
67        self.is_pub = is_pub;
68        self
69    }
70
71    /// Set the name of this function.
72    pub fn set_name<S: ToString>(&mut self, name: S) -> &mut Self {
73        self.name = name.to_string();
74        self
75    }
76}
77
78impl SrcCode for FunctionSignature {
79    fn generate(&self) -> String {
80        let template = r#"
81        {% if self.is_pub %}pub {% endif %}{% if self.is_async %}async {% endif %}fn {{ self.name }}{% if has_generics %}<{{ generic_keys | join(sep=", ") }}>{% endif %}({{ parameters | join(sep=", ") }}) -> {{ return_ty }}{% if has_generics %}
82            where
83                {% for generic in generics %}{{ generic.generic }}: {{ generic.traits | join(sep=" + ") }},
84                {% endfor %}{% endif %}"#;
85        let mut context = Context::new();
86        context.insert("self", &self);
87        context.insert(
88            "return_ty",
89            &self.return_ty.as_ref().unwrap_or(&"()".to_string()),
90        );
91        context.insert("has_generics", &!self.generics.is_empty());
92        context.insert("generics", &self.generics.generics);
93        context.insert(
94            "generic_keys",
95            &self
96                .generics
97                .generics
98                .iter()
99                .map(|g| g.generic.clone())
100                .collect::<Vec<String>>(),
101        );
102        context.insert(
103            "parameters",
104            &self
105                .parameters
106                .iter()
107                .map(|param| format!("{}: {}", param.name, param.ty))
108                .collect::<Vec<String>>(),
109        );
110        Tera::one_off(template, &context, false).unwrap()
111    }
112}
113
114/// Represents the function/method's body
115#[derive(Default, Serialize, Clone)]
116pub struct FunctionBody {
117    body: String,
118}
119
120impl SrcCode for FunctionBody {
121    fn generate(&self) -> String {
122        let template = r#"
123            {{ body }}
124        "#;
125        let mut ctx = Context::new();
126        ctx.insert("body", &self.body);
127        Tera::one_off(template, &ctx, false).unwrap()
128    }
129}
130
131impl<S> From<S> for FunctionBody
132where
133    S: ToString,
134{
135    fn from(body: S) -> FunctionBody {
136        FunctionBody {
137            body: body.to_string(),
138        }
139    }
140}
141
142impl Function {
143    /// Create a new function
144    pub fn new<S: ToString>(name: S) -> Self {
145        let mut f = Self::default();
146        f.signature.name = name.to_string();
147        f
148    }
149
150    /// Add a new parameter to this function
151    pub fn add_parameter(&mut self, param: Parameter) -> &mut Self {
152        self.signature.parameters.push(param);
153        self
154    }
155    /// Add a new trait bound generic to this function
156    pub fn add_generic(&mut self, generic: Generic) -> &mut Self {
157        self.signature.generics.add_generic(generic);
158        self
159    }
160    /// Set the return type of this function
161    pub fn set_return_ty<S: ToString>(&mut self, ty: S) -> &mut Self {
162        self.signature.return_ty = Some(ty.to_string());
163        self
164    }
165    /// Set if this function is public
166    pub fn set_is_pub(&mut self, is_pub: bool) -> &mut Self {
167        self.signature.set_is_pub(is_pub);
168        self
169    }
170    /// Set if this function is async
171    pub fn set_is_async(&mut self, is_async: bool) -> &mut Self {
172        self.signature.set_is_async(is_async);
173        self
174    }
175    /// Set the body of the function, this should be valid Rust source code syntax.
176    pub fn set_body<S: Into<FunctionBody>>(&mut self, body: S) -> &mut Self {
177        self.body = body.into();
178        self
179    }
180}
181
182/// Represents a single parameter to a `Function`
183#[derive(Serialize, Default, Clone)]
184pub struct Parameter {
185    name: String,
186    ty: String,
187}
188impl Parameter {
189    /// Create a new parameter
190    ///
191    /// Example
192    /// -------
193    /// ```
194    /// use proffer::*;
195    ///
196    /// let param = Parameter::new("foo", "usize").generate();
197    /// let expected = "foo: usize";
198    /// assert_eq!(expected, &param);
199    /// ```
200    ///
201    pub fn new<S: ToString>(name: S, ty: S) -> Self {
202        Self {
203            name: name.to_string(),
204            ty: ty.to_string(),
205        }
206    }
207}
208
209impl SrcCode for Parameter {
210    fn generate(&self) -> String {
211        let template = "{{ self.name }}: {{ self.ty }}";
212        let mut ctx = Context::new();
213        ctx.insert("self", &self);
214        Tera::one_off(template, &ctx, false).unwrap()
215    }
216}
217
218impl SrcCode for Function {
219    fn generate(&self) -> String {
220        let template = r#"
221        {{ function_signature }}
222        {
223            {{ body }}
224        }
225        "#;
226        let mut context = Context::new();
227        context.insert("body", &self.body.generate());
228        context.insert("function_signature", &self.signature.generate());
229        Tera::one_off(template, &context, false).unwrap()
230    }
231}