oneline_template/template/
template_builder.rs

1use crate::template::template_build_error::TemplateBuildError;
2use crate::template::template::Template;
3use crate::template::argument_types_differ_error::ArgumentTypesDifferError;
4use crate::function_executor::FunctionExecutor;
5use crate::template::syntax::function_call_token::FunctionCallToken;
6use crate::function_executor::FunctionArgumentType;
7use crate::template::syntax::syntax::Syntax;
8use crate::template::syntax::function_call_argument_token::FunctionCallArgumentToken;
9use crate::template::syntax::token::Token;
10use crate::template::syntax::template_token::TemplateToken;
11use crate::functions;
12use std::str::FromStr;
13use std::collections::HashMap;
14
15
16/// Creates template.
17pub struct TemplateBuilder {
18    functions: HashMap<String, Box<dyn FunctionExecutor>>,
19}
20
21impl TemplateBuilder {
22    /// Creates template builder WITH default functions.
23    pub fn new() -> TemplateBuilder {
24        return TemplateBuilder::new_empty()
25            .with_function(functions::bool::ToString)
26            .with_function(functions::bool::UnwrapOr)
27            .with_function(functions::float::ToString)
28            .with_function(functions::int::Abs)
29            .with_function(functions::int::Hex)
30            .with_function(functions::int::HexFmt)
31            .with_function(functions::int::ToString)
32            .with_function(functions::string::Trim)
33            .with_function(functions::string::UnwrapOr)
34            .with_function(functions::string::TrimStart)
35            .with_function(functions::string::TrimEnd)
36            .with_function(functions::string::SubStr)
37            .with_function(functions::uint::Hex)
38            .with_function(functions::uint::HexFmt)
39            .with_function(functions::uint::ToString)
40            .with_function(functions::debug::DebugType);
41    }
42
43    /// Creates template builder WITHOUT default functions.
44    pub fn new_empty() -> TemplateBuilder {
45        return TemplateBuilder {
46            functions: HashMap::new(),
47        }
48    }
49
50    /// Adds function executor to template builder.
51    pub fn with_function(mut self, function_executor: impl FunctionExecutor + 'static) -> Self {
52        let function_executor: Box<dyn FunctionExecutor> = Box::new(function_executor);
53        let schema = function_executor.schema();
54        let function_name = schema.get_function_name().clone();
55        let _ = self.functions.insert(function_name, function_executor);
56        return self;
57    }
58
59    fn validate_function_call(&self, function_call: &FunctionCallToken) -> Result<(), TemplateBuildError> {
60        let function_name = function_call.get_function_name().as_string_ref();
61        let function_executor = match self.functions.get(function_name) {
62            Some(function_executor) => {function_executor},
63            None => {
64                return Err(TemplateBuildError::FunctionNotFound(function_name.to_string()));
65            },
66        };
67        let schema = function_executor.schema();
68        if function_call.get_arguments().len() != schema.get_arguments().len() {
69            return Err(TemplateBuildError::ArgumentsLengthDiffer(function_name.to_string()));
70        }
71        let iterator = function_call.get_arguments().iter().zip(schema.get_arguments().iter());
72        for (argument_index, (actual_argument, expected_argument)) in iterator.enumerate() {
73            let actual_argument_type = match actual_argument {
74                FunctionCallArgumentToken::String(..) => FunctionArgumentType::String,
75                FunctionCallArgumentToken::Bool(..) => FunctionArgumentType::Bool,
76                FunctionCallArgumentToken::UInt(..) => FunctionArgumentType::UInt,
77                FunctionCallArgumentToken::Int(..) => FunctionArgumentType::Int,
78            };
79            let expected_argument_type = expected_argument.get_type();
80            if &actual_argument_type != expected_argument_type {
81                return Err(ArgumentTypesDifferError::new(function_name.to_string(), argument_index).into());
82            }
83        }
84        return Ok(());
85    }
86
87    fn validate_template_token(&self, token: &TemplateToken) -> Result<(), TemplateBuildError> {
88        let token = token.get_field_read_token();
89        for function_call in token.get_function_calls().iter() {
90            self.validate_function_call(function_call)?;
91        }
92        return Ok(());
93    }
94
95    fn validate_syntax(&self, syntax: &Syntax) -> Result<(), TemplateBuildError> {
96        for token in syntax.iter_tokens() {
97            match token {
98                Token::Text(..) => {},
99                Token::Template(token) => {
100                    self.validate_template_token(token)?;
101                },
102            }
103        }
104        return Ok(());
105    }
106
107    /// Creates template using passed template format.
108    pub fn build(self, format: &str) -> Result<Template, TemplateBuildError> {
109        let syntax = Syntax::from_str(format)?;
110        self.validate_syntax(&syntax)?;
111        let template = Template::new(syntax, self.functions);
112        return Ok(template);
113    }
114}