use serde::Serialize;
use tera::{Context, Tera};
use crate::traits::SrcCode;
use crate::{Generic, Generics};
#[derive(Default, Serialize, Clone)]
pub struct Function {
signature: FunctionSignature,
body: FunctionBody,
}
#[derive(Default, Serialize, Clone)]
pub struct FunctionSignature {
name: String,
is_pub: bool,
is_async: bool,
parameters: Vec<Parameter>,
generics: Generics,
return_ty: Option<String>,
}
impl FunctionSignature {
pub fn new<S: ToString>(name: S) -> Self {
let mut f = Self::default();
f.name = name.to_string();
f
}
pub fn set_is_async(&mut self, is_async: bool) -> &mut Self {
self.is_async = is_async;
self
}
pub fn add_parameter(&mut self, param: Parameter) -> &mut Self {
self.parameters.push(param);
self
}
pub fn add_generic(&mut self, generic: Generic) -> &mut Self {
self.generics.add_generic(generic);
self
}
pub fn set_return_ty<S: ToString>(&mut self, ty: Option<S>) -> &mut Self {
self.return_ty = ty.map(|s| s.to_string());
self
}
pub fn set_is_pub(&mut self, is_pub: bool) -> &mut Self {
self.is_pub = is_pub;
self
}
pub fn set_name<S: ToString>(&mut self, name: S) -> &mut Self {
self.name = name.to_string();
self
}
}
impl SrcCode for FunctionSignature {
fn generate(&self) -> String {
let template = r#"
{% 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 %}
where
{% for generic in generics %}{{ generic.generic }}: {{ generic.traits | join(sep=" + ") }},
{% endfor %}{% endif %}"#;
let mut context = Context::new();
context.insert("self", &self);
context.insert(
"return_ty",
&self.return_ty.as_ref().unwrap_or(&"()".to_string()),
);
context.insert("has_generics", &!self.generics.is_empty());
context.insert("generics", &self.generics.generics);
context.insert(
"generic_keys",
&self
.generics
.generics
.iter()
.map(|g| g.generic.clone())
.collect::<Vec<String>>(),
);
context.insert(
"parameters",
&self
.parameters
.iter()
.map(|param| format!("{}: {}", param.name, param.ty))
.collect::<Vec<String>>(),
);
Tera::one_off(template, &context, false).unwrap()
}
}
#[derive(Default, Serialize, Clone)]
pub struct FunctionBody {
body: String,
}
impl SrcCode for FunctionBody {
fn generate(&self) -> String {
let template = r#"
{{ body }}
"#;
let mut ctx = Context::new();
ctx.insert("body", &self.body);
Tera::one_off(template, &ctx, false).unwrap()
}
}
impl<S> From<S> for FunctionBody
where
S: ToString,
{
fn from(body: S) -> FunctionBody {
FunctionBody {
body: body.to_string(),
}
}
}
impl Function {
pub fn new<S: ToString>(name: S) -> Self {
let mut f = Self::default();
f.signature.name = name.to_string();
f
}
pub fn add_parameter(&mut self, param: Parameter) -> &mut Self {
self.signature.parameters.push(param);
self
}
pub fn add_generic(&mut self, generic: Generic) -> &mut Self {
self.signature.generics.add_generic(generic);
self
}
pub fn set_return_ty<S: ToString>(&mut self, ty: S) -> &mut Self {
self.signature.return_ty = Some(ty.to_string());
self
}
pub fn set_is_pub(&mut self, is_pub: bool) -> &mut Self {
self.signature.set_is_pub(is_pub);
self
}
pub fn set_is_async(&mut self, is_async: bool) -> &mut Self {
self.signature.set_is_async(is_async);
self
}
pub fn set_body<S: Into<FunctionBody>>(&mut self, body: S) -> &mut Self {
self.body = body.into();
self
}
}
#[derive(Serialize, Default, Clone)]
pub struct Parameter {
name: String,
ty: String,
}
impl Parameter {
pub fn new<S: ToString>(name: S, ty: S) -> Self {
Self {
name: name.to_string(),
ty: ty.to_string(),
}
}
}
impl SrcCode for Parameter {
fn generate(&self) -> String {
let template = "{{ self.name }}: {{ self.ty }}";
let mut ctx = Context::new();
ctx.insert("self", &self);
Tera::one_off(template, &ctx, false).unwrap()
}
}
impl SrcCode for Function {
fn generate(&self) -> String {
let template = r#"
{{ function_signature }}
{
{{ body }}
}
"#;
let mut context = Context::new();
context.insert("body", &self.body.generate());
context.insert("function_signature", &self.signature.generate());
Tera::one_off(template, &context, false).unwrap()
}
}