liquid_lib/stdlib/tags/
increment_tags.rs1use std::io::Write;
2
3use liquid_core::error::ResultLiquidReplaceExt;
4use liquid_core::model::{Value, ValueView};
5use liquid_core::Language;
6use liquid_core::Renderable;
7use liquid_core::Result;
8use liquid_core::Runtime;
9use liquid_core::{ParseTag, TagReflection, TagTokenIter};
10
11#[derive(Copy, Clone, Debug, Default)]
12pub struct IncrementTag;
13
14impl IncrementTag {
15 pub fn new() -> Self {
16 Self
17 }
18}
19
20impl TagReflection for IncrementTag {
21 fn tag(&self) -> &'static str {
22 "increment"
23 }
24
25 fn description(&self) -> &'static str {
26 ""
27 }
28}
29
30impl ParseTag for IncrementTag {
31 fn parse(
32 &self,
33 mut arguments: TagTokenIter<'_>,
34 _options: &Language,
35 ) -> Result<Box<dyn Renderable>> {
36 let id = arguments
37 .expect_next("Identifier expected.")?
38 .expect_identifier()
39 .into_result()?
40 .to_owned()
41 .into();
42
43 arguments.expect_nothing()?;
45
46 Ok(Box::new(Increment { id }))
47 }
48
49 fn reflection(&self) -> &dyn TagReflection {
50 self
51 }
52}
53
54#[derive(Clone, Debug)]
55struct Increment {
56 id: liquid_core::model::KString,
57}
58
59impl Renderable for Increment {
60 fn render_to(&self, writer: &mut dyn Write, runtime: &dyn Runtime) -> Result<()> {
61 let mut val = runtime
62 .get_index(&self.id)
63 .and_then(|i| i.as_scalar().and_then(|i| i.to_integer()))
64 .unwrap_or(0);
65
66 write!(writer, "{val}").replace("Failed to render")?;
67 val += 1;
68 runtime.set_index(self.id.clone(), Value::scalar(val));
69 Ok(())
70 }
71}
72
73#[derive(Copy, Clone, Debug, Default)]
74pub struct DecrementTag;
75
76impl DecrementTag {
77 pub fn new() -> Self {
78 Self
79 }
80}
81
82impl TagReflection for DecrementTag {
83 fn tag(&self) -> &'static str {
84 "decrement"
85 }
86
87 fn description(&self) -> &'static str {
88 ""
89 }
90}
91
92impl ParseTag for DecrementTag {
93 fn parse(
94 &self,
95 mut arguments: TagTokenIter<'_>,
96 _options: &Language,
97 ) -> Result<Box<dyn Renderable>> {
98 let id = arguments
99 .expect_next("Identifier expected.")?
100 .expect_identifier()
101 .into_result()?
102 .to_owned()
103 .into();
104
105 arguments.expect_nothing()?;
107
108 Ok(Box::new(Decrement { id }))
109 }
110
111 fn reflection(&self) -> &dyn TagReflection {
112 self
113 }
114}
115
116#[derive(Clone, Debug)]
117struct Decrement {
118 id: liquid_core::model::KString,
119}
120
121impl Renderable for Decrement {
122 fn render_to(&self, writer: &mut dyn Write, runtime: &dyn Runtime) -> Result<()> {
123 let mut val = runtime
124 .get_index(&self.id)
125 .and_then(|i| i.as_scalar().and_then(|i| i.to_integer()))
126 .unwrap_or(0);
127
128 val -= 1;
129 write!(writer, "{val}").replace("Failed to render")?;
130 runtime.set_index(self.id.clone(), Value::scalar(val));
131 Ok(())
132 }
133}
134
135#[cfg(test)]
136mod test {
137 use super::*;
138
139 use liquid_core::parser;
140 use liquid_core::runtime;
141 use liquid_core::runtime::RuntimeBuilder;
142
143 use crate::stdlib;
144
145 fn options() -> Language {
146 let mut options = Language::default();
147 options
148 .tags
149 .register("assign".to_owned(), stdlib::AssignTag.into());
150 options
151 .tags
152 .register("increment".to_owned(), IncrementTag.into());
153 options
154 .tags
155 .register("decrement".to_owned(), DecrementTag.into());
156 options
157 }
158
159 #[test]
160 fn increment() {
161 let text = "{% increment val %}{{ val }}";
162 let template = parser::parse(text, &options())
163 .map(runtime::Template::new)
164 .unwrap();
165
166 let runtime = RuntimeBuilder::new().build();
167 let output = template.render(&runtime).unwrap();
168 assert_eq!(output, "01");
169 }
170
171 #[test]
172 fn decrement() {
173 let text = "{% decrement val %}{{ val }}";
174 let template = parser::parse(text, &options())
175 .map(runtime::Template::new)
176 .unwrap();
177
178 let runtime = RuntimeBuilder::new().build();
179 let output = template.render(&runtime).unwrap();
180 assert_eq!(output, "-1-1");
181 }
182
183 #[test]
184 fn increment_and_decrement() {
185 let text = "{% increment val %}{% increment val %}{% decrement val %}{% decrement val %}";
186 let template = parser::parse(text, &options())
187 .map(runtime::Template::new)
188 .unwrap();
189
190 let runtime = RuntimeBuilder::new().build();
191 let output = template.render(&runtime).unwrap();
192 assert_eq!(output, "0110");
193 }
194
195 #[test]
196 fn assign_and_increment() {
197 let text = "{%- assign val = 9 -%}{% increment val %}{% increment val %}{{ val }}";
198 let template = parser::parse(text, &options())
199 .map(runtime::Template::new)
200 .unwrap();
201
202 let runtime = RuntimeBuilder::new().build();
203 let output = template.render(&runtime).unwrap();
204 assert_eq!(output, "019");
205 }
206}