1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::collections::BTreeMap;

use serde_json::value::Value as Json;

use crate::error::RenderError;
use crate::local_vars::LocalVars;

#[derive(Clone, Debug)]
pub enum BlockParamHolder {
    // a reference to certain context value
    Path(Vec<String>),
    // an actual value holder
    Value(Json),
}

impl BlockParamHolder {
    pub fn value(v: Json) -> BlockParamHolder {
        BlockParamHolder::Value(v)
    }

    pub fn path(r: Vec<String>) -> BlockParamHolder {
        BlockParamHolder::Path(r)
    }
}

/// A map holds block parameters. The parameter can be either a value or a reference
#[derive(Clone, Debug, Default)]
pub struct BlockParams<'reg> {
    data: BTreeMap<&'reg str, BlockParamHolder>,
}

impl<'reg> BlockParams<'reg> {
    /// Create a empty block parameter map.
    pub fn new() -> BlockParams<'reg> {
        BlockParams::default()
    }

    /// Add a path reference as the parameter. The `path` is a vector of path
    /// segments the relative to current block's base path.
    pub fn add_path(&mut self, k: &'reg str, path: Vec<String>) -> Result<(), RenderError> {
        self.data.insert(k, BlockParamHolder::path(path));
        Ok(())
    }

    /// Add a value as parameter.
    pub fn add_value(&mut self, k: &'reg str, v: Json) -> Result<(), RenderError> {
        self.data.insert(k, BlockParamHolder::value(v));
        Ok(())
    }

    /// Get a block parameter by its name.
    pub fn get(&self, k: &str) -> Option<&BlockParamHolder> {
        self.data.get(k)
    }
}

/// A data structure holds contextual data for current block scope.
#[derive(Debug, Clone, Default)]
pub struct BlockContext<'rc> {
    /// the base_path of current block scope
    base_path: Vec<String>,
    /// the base_value of current block scope, when the block is using a
    /// constant or derived value as block base
    base_value: Option<Json>,
    /// current block context variables
    block_params: BlockParams<'rc>,
    /// local variables in current context
    local_variables: LocalVars,
}

impl<'rc> BlockContext<'rc> {
    /// create a new `BlockContext` with default data
    pub fn new() -> BlockContext<'rc> {
        BlockContext::default()
    }

    /// set a local variable into current scope
    pub fn set_local_var(&mut self, name: &str, value: Json) {
        self.local_variables.put(name, value);
    }

    /// Get mutable access to the local variables
    pub fn local_variables_mut(&mut self) -> &mut LocalVars {
        &mut self.local_variables
    }

    /// get a local variable from current scope
    pub fn get_local_var(&self, name: &str) -> Option<&Json> {
        self.local_variables.get(name)
    }

    /// borrow a reference to current scope's base path
    /// all paths inside this block will be relative to this path
    pub fn base_path(&self) -> &Vec<String> {
        &self.base_path
    }

    /// borrow a mutable reference to the base path
    pub fn base_path_mut(&mut self) -> &mut Vec<String> {
        &mut self.base_path
    }

    /// borrow the base value
    pub fn base_value(&self) -> Option<&Json> {
        self.base_value.as_ref()
    }

    /// set the base value
    pub fn set_base_value(&mut self, value: Json) {
        self.base_value = Some(value);
    }

    /// Get a block parameter from this block.
    /// Block parameters needed to be supported by the block helper.
    /// The typical syntax for block parameter is:
    ///
    /// ```skip
    /// {{#myblock param1 as |block_param1|}}
    ///    ...
    /// {{/myblock}}
    /// ```
    ///
    pub fn get_block_param(&self, block_param_name: &str) -> Option<&BlockParamHolder> {
        self.block_params.get(block_param_name)
    }

    /// Set a block parameter into this block.
    pub fn set_block_params(&mut self, block_params: BlockParams<'rc>) {
        self.block_params = block_params;
    }
}