1use crate::renderer::{Attributes, Renderer, RenderValue, RenderError, basic_html_tag};
2use crate::context::{ContextValue, RenderContext};
3use crate::template::{TemplateExprNode, TemplateTag};
4
5
6pub(crate) fn do_html(attrs: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
7 let mut v: Vec<RenderValue> = vec!["<!doctype html5>".into()];
8 v.push(basic_html_tag("html".into(), &attrs, &expr, renderer, context)?.into());
9 Ok(v.into())
10}
11
12pub(crate) fn do_is_set(_: Attributes, expr: &[TemplateExprNode], _render: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
13 match expr.get(0) {
14 Some(TemplateExprNode::Identifier(ident)) => {
15 Ok(match context.get(ident) {
16 Some(_) => RenderValue::Boolean(true),
17 None => RenderValue::Boolean(false)
18 })
19 },
20 _ => Err(RenderError::IsSet("expected identifier".into(), expr.to_vec()))
21 }
22}
23
24pub(crate) fn do_cmp_op<F>(_: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext, op: F) -> Result<RenderValue, RenderError>
25where
26 F: FnOnce(ContextValue, ContextValue) -> bool,
27{
28 let exp1 = expr.get(0)
29 .and_then(|e| {
30 match e {
31 TemplateExprNode::Identifier(ident) => Some(context.get(ident).cloned().unwrap_or(ContextValue::String(ident.clone()))),
32 TemplateExprNode::Integer(i) => Some(ContextValue::Integer(*i)),
33 TemplateExprNode::Tag(_tag) => Some(renderer.evaluate(e, context).unwrap().into()),
34 }
35 })
36 .ok_or_else(|| RenderError::Cmp("missing expr 1".into(), expr.to_vec()))?;
37 let exp2 = expr.get(1)
38 .and_then(|e| {
39 match e {
40 TemplateExprNode::Identifier(ident) => Some(context.get(ident).cloned().unwrap_or(ContextValue::String(ident.clone()))),
41 TemplateExprNode::Integer(i) => Some(ContextValue::Integer(*i)),
42 TemplateExprNode::Tag(_tag) => Some(renderer.evaluate(e, context).ok()?.into()),
43 }
44 })
45 .ok_or_else(|| RenderError::Cmp("missing expr 2".into(), expr.to_vec()))?;
46
47 Ok(op(exp1, exp2).into())
48}
49
50
51pub(crate) fn do_math_op<F>(_: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext, op: F) -> Result<RenderValue, RenderError>
52where
53 F: FnOnce(i64, i64) -> i64
54{
55 let exp1 = expr.get(0)
56 .and_then(|e| renderer.evaluate(e, context).ok())
57 .and_then(|rv| rv.as_int())
58 .ok_or_else(|| RenderError::Math("missing expr 1".into(), expr.to_vec()))?;
59
60 let exp2 = expr.get(1)
61 .and_then(|e| renderer.evaluate(e, context).ok())
62 .and_then(|rv| rv.as_int())
63 .ok_or_else(|| RenderError::Math("missing expr 2".into(), expr.to_vec()))?;
64
65 Ok(op(exp1, exp2).into())
66}
67
68
69pub(crate) fn do_if(_: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
70 let conditional = expr.get(0)
71 .ok_or_else(|| RenderError::If("condition not found".into(), expr.to_vec()))?;
72
73 let result = renderer.evaluate(conditional, context)?;
74
75 let is_true = match result {
76 RenderValue::Boolean(b) => b,
77 RenderValue::String(s) if s != "" => true,
78 RenderValue::Integer(i) if i != 0 => true,
79 _ => false
80 };
81
82 Ok(
83 if is_true {
84 renderer.evaluate(expr
85 .get(1)
86 .ok_or_else(|| RenderError::If("code block not found".into(), expr.to_vec()))?,
87 context)?
88 }
89 else {
90 match expr.get(2) {
91 Some(e) => renderer.evaluate(e, context)?,
92 None => RenderValue::Empty,
93 }
94 })
95}
96
97pub(crate) fn do_case(_: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
98 let condition = expr.get(0)
99 .ok_or_else(|| RenderError::Case("variant not found".into(), expr.to_vec()))?;
100
101 let switch_value = context.get("__switch")
102 .ok_or_else(|| RenderError::Case("builtin switch variable not found?".into(), expr.to_vec()))?;
103
104 let body = expr.get(1..)
105 .unwrap_or_default();
106
107 match (condition, switch_value) {
108 (TemplateExprNode::Identifier(condition_str), ContextValue::String(switch_str)) if condition_str == switch_str => {
109 renderer.evaluate_multiple(body, context)
110 },
111 _ => return Ok(RenderValue::Empty)
112 }
113}
114
115pub(crate) fn do_switch(_: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
116 let variable = renderer.evaluate(expr.get(0)
117 .ok_or_else(|| RenderError::Switch("variable not found".into(), expr.to_vec()))?,
118 context)?;
119 let cases = expr.get(1..);
120 let mut context = context.clone();
121 context.insert("__switch", &variable);
122 Ok(cases.iter()
123 .map(|case| {
124 renderer.evaluate_multiple(*case, &context)
125 })
126 .collect::<Result<Vec<_>, _>>()?
127 .into())
128}
129
130
131fn parse_range(tag: &TemplateTag, renderer: &Renderer, context: &RenderContext) -> Option<ContextValue> {
132 let min = tag.children.get(0)
133 .and_then(|e| renderer.evaluate(e, context).ok())
134 .and_then(|e| e.as_int())?;
135 let max = tag.children.get(1)
136 .and_then(|e| renderer.evaluate(e, context).ok())
137 .and_then(|e| e.as_int())?;
138 let step = tag.children.get(2)
139 .and_then(|e| renderer.evaluate(e, context).ok())
140 .and_then(|e| e.as_int())
141 .unwrap_or(1) as usize;
142
143 let range = (min..max)
144 .step_by(step)
145 .map(Into::into)
146 .collect();
147
148 Some(ContextValue::Vec(range))
149}
150
151pub(crate) fn do_for(attrs: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
152 let in_position = expr.iter()
153 .position(|b| {
154 match b {
155 TemplateExprNode::Identifier(ident) if ident == "in" => true,
156 _ => false,
157 }
158 });
159
160 if let Some(in_position) = in_position {
161 let iterable = expr.get(in_position+1)
162 .map(|e| {
163 match e {
164 TemplateExprNode::Identifier(ident) => {
165 crate::renderer::expand_variable(ident, renderer, context)
166 .map(|k| k.into())
167 },
168 TemplateExprNode::Tag(tag) if tag.tag == "range" => {
169 parse_range(tag, renderer, context)
170 .ok_or_else(|| RenderError::For("invalid range".into(), attrs.clone(), expr.to_vec()))
171 },
172 _ => Err(RenderError::For("iteration variable is not a valid type".into(), attrs.clone(), expr.to_vec()))
173 }
174 })
175 .ok_or_else(|| RenderError::For("no iteration variable specified".into(), attrs.clone(), expr.to_vec()))??;
176 let body = expr.get(in_position+2..)
177 .unwrap_or_default();
178
179 match iterable {
180 ContextValue::Vec(v) => {
181 enum IterType {
182 Normal(String),
183 Enum(String, String),
184 }
185
186 let val = expr.get(in_position-1)
187 .and_then(|e| {
188 match e {
189 TemplateExprNode::Identifier(_) => renderer.evaluate(e, context).map(|e| IterType::Normal(e.finalize())).ok(),
190 TemplateExprNode::Tag(tag) if tag.tag == "enumerate" => {
191 let index = tag.children.get(0)
192 .and_then(TemplateExprNode::as_identifier)?;
193 let iter = tag.children.get(1)
194 .and_then(TemplateExprNode::as_identifier)?;
195 Some(IterType::Enum(iter.clone(), index.clone()))
196 },
197 _ => None
198 }
199 })
200 .ok_or_else(|| RenderError::For("missing variable to iterate over".into(), attrs, expr.to_vec()))?;
201
202 let mut second_context = context.clone();
203 Ok(v.iter()
204 .enumerate()
205 .map(|(i, value)| {
206 match &val {
207 IterType::Normal(val) => {
208 second_context.insert(val.clone(), value.clone());
209 }
210 IterType::Enum(iter, index) => {
211 second_context.insert(iter.clone(), value.clone());
212 second_context.insert(index.clone(), i);
213 }
214 }
215 renderer.evaluate_multiple(body, &second_context)
216 })
217 .collect::<Result<Vec<_>, RenderError>>()?
218 .into())
219 },
220 ContextValue::Object(o) => {
221 let key_var = expr.get(in_position-2)
222 .and_then(|a| renderer.evaluate(a, context).ok())
223 .map(|e| e.finalize())
224 .ok_or_else(|| RenderError::For("missing key variable to iterate over".into(), attrs.clone(), expr.to_vec()))?;
225 let value_var = expr.get(in_position-1)
226 .and_then(|a| renderer.evaluate(a, context).ok())
227 .map(|e| e.finalize())
228 .ok_or_else(|| RenderError::For("missing value variable to iterate over".into(), attrs.clone(), expr.to_vec()))?;
229 let mut second_context = context.clone();
230 Ok(o.0.iter()
231 .map(|(key, value)| {
232 second_context.insert(key_var.clone(), ContextValue::String(key.clone()));
233 second_context.insert(value_var.clone(), value.clone());
234 renderer.evaluate_multiple(body, &second_context)
235 })
236 .collect::<Result<Vec<_>, RenderError>>()?
237 .into())
238 },
239 _ => Err(RenderError::For("element is not iterable".into(), attrs, expr.to_vec()))
240 }
241 }
242 else {
243 Err(RenderError::For("invalid syntax".into(), attrs, expr.to_vec()))
244 }
245}
246
247pub(crate) fn do_get(_: Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
248 let indexable = expr.get(0)
249 .and_then(|e| renderer.evaluate(e, context).ok())
250 .unwrap();
251
252 let index = expr.get(1)
253 .and_then(|e| renderer.evaluate(e, context).ok())
254 .unwrap();
255
256 match (indexable, index){
257 (RenderValue::Vec(v), RenderValue::Integer(i)) => {
258 Ok(v.get(i as usize)
259 .ok_or_else(|| RenderError::Get("array out of bounds".into(), expr.to_vec()))?
260 .clone())
261
262 },
263 (RenderValue::Object(o), RenderValue::String(s)) => {
264 Ok(o.get(&s).ok_or_else(|| RenderError::Get("array out of bounds".into(), expr.to_vec()))?.clone())
265 },
266 _ => Err(RenderError::Get("invalid index/indexable".into(), expr.to_vec()))
267 }
268}