codegenr_lib/helpers/
getset.rs

1use super::handlebars_ext::HandlebarsExt;
2use handlebars::{HelperDef, RenderError, Renderable};
3use serde_json::Value;
4use std::{
5  collections::HashMap,
6  sync::{Arc, RwLock},
7};
8
9pub const GET_HELPER: &str = "get";
10pub const SET_HELPER: &str = "set";
11pub const CLEAR_HELPER: &str = "clear";
12pub const IF_SET_HELPER: &str = "if_set";
13pub const WITH_SET_HELPER: &str = "with_set";
14
15/// Gets a value from the key/value store
16/// ```
17/// # use codegenr_lib::helpers::*;
18/// # use serde_json::json;
19/// assert_eq!(
20///   exec_template(json!({}), r#"{{set "k" "v"}}{{get "k"}}"#),
21///   "v"
22/// );
23/// assert_eq!(
24///   exec_template(json!({}), r#"{{set "" "v"}}{{get ""}}"#),
25///   "v"
26/// );
27/// assert_eq!(
28///   exec_template(json!({}), r#"{{set "k" 42}}{{get "k"}}"#),
29///   "42"
30/// );
31/// ```
32///
33/// An error will be raise if a non existing key is asked
34/// ```should_panic
35/// # use serde_json::json;
36/// # use codegenr_lib::helpers::*;
37/// exec_template(json!({}), r#"{{get "plop"}}"#);
38/// ```
39pub struct GetHelper {
40  values: Arc<RwLock<HashMap<String, Value>>>,
41}
42
43impl GetHelper {
44  pub fn new(values: &Arc<RwLock<HashMap<String, Value>>>) -> Self {
45    Self { values: values.clone() }
46  }
47}
48
49impl HelperDef for GetHelper {
50  fn call_inner<'reg: 'rc, 'rc>(
51    &self,
52    h: &handlebars::Helper<'reg, 'rc>,
53    _: &'reg handlebars::Handlebars<'reg>,
54    _: &'rc handlebars::Context,
55    _: &mut handlebars::RenderContext<'reg, 'rc>,
56  ) -> Result<handlebars::ScopedJson<'reg, 'rc>, RenderError> {
57    h.ensure_arguments_count(1, GET_HELPER)?;
58    let key = h.get_param_as_str_or_fail(0, GET_HELPER)?.to_string();
59    let value = get_value(&self.values, &key, GET_HELPER)?;
60    match value {
61      Some(v) => Ok(v.into()),
62      None => Err(RenderError::new(format!(
63        "Value is not set for key `{}` in `{}` helper.",
64        key, GET_HELPER
65      ))),
66    }
67  }
68}
69
70/// Sets a value in the key/value store
71/// see [`GetHelper`] for more examples
72pub struct SetHelper {
73  values: Arc<RwLock<HashMap<String, Value>>>,
74}
75
76impl SetHelper {
77  pub fn new(values: &Arc<RwLock<HashMap<String, Value>>>) -> Self {
78    Self { values: values.clone() }
79  }
80}
81
82impl HelperDef for SetHelper {
83  fn call<'reg: 'rc, 'rc>(
84    &self,
85    h: &handlebars::Helper<'reg, 'rc>,
86    _handle: &'reg handlebars::Handlebars<'reg>,
87    _ctx: &'rc handlebars::Context,
88    _render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
89    _out: &mut dyn handlebars::Output,
90  ) -> handlebars::HelperResult {
91    h.ensure_arguments_count(2, SET_HELPER)?;
92    let key = h.get_param_as_str_or_fail(0, SET_HELPER)?.to_string();
93    let value = h.get_param_as_json_or_fail(1, SET_HELPER)?;
94    set_value(&self.values, key, value.clone(), SET_HELPER)?;
95    Ok(())
96  }
97}
98
99/// Sets a value in the key/value store and clear it at the end of the block
100///```
101/// # use codegenr_lib::helpers::*;
102/// # use serde_json::json;
103/// assert_eq!(
104///   exec_template(json!({ "key": "value" }), r#"{{#with_set "key" key}}{{get "key"}}{{/with_set}}"#),
105///   "value"
106/// );
107///```
108/// see [`GetHelper`] for more examples
109pub struct WithSetHelper {
110  values: Arc<RwLock<HashMap<String, Value>>>,
111}
112
113impl WithSetHelper {
114  pub fn new(values: &Arc<RwLock<HashMap<String, Value>>>) -> Self {
115    Self { values: values.clone() }
116  }
117}
118
119impl HelperDef for WithSetHelper {
120  fn call<'reg: 'rc, 'rc>(
121    &self,
122    h: &handlebars::Helper<'reg, 'rc>,
123    handle: &'reg handlebars::Handlebars<'reg>,
124    ctx: &'rc handlebars::Context,
125    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
126    out: &mut dyn handlebars::Output,
127  ) -> handlebars::HelperResult {
128    h.ensure_arguments_count(2, WITH_SET_HELPER)?;
129    let key = h.get_param_as_str_or_fail(0, WITH_SET_HELPER)?.to_string();
130    let value = h.get_param_as_json_or_fail(1, WITH_SET_HELPER)?;
131    set_value(&self.values, key.clone(), value.clone(), WITH_SET_HELPER)?;
132
133    if let Some(t) = h.template() {
134      t.render(handle, ctx, render_ctx, out)?;
135    }
136
137    rem_value(&self.values, &key, WITH_SET_HELPER)
138  }
139}
140
141/// Sets a value in the key/value store and clear it at the end of the block
142///```
143/// # use codegenr_lib::helpers::*;
144/// # use serde_json::json;
145/// assert_eq!(
146///   exec_template(json!({}), r#"{{set "k" 42}}{{#if_set "k"}}OK{{/if_set}}"#),
147///   "OK"
148/// );
149/// assert_eq!(
150///   exec_template(json!({}), r#"{{#if_set "k"}}OK{{else}}NOK{{/if_set}}"#),
151///   "NOK"
152/// );
153///```
154/// see [`GetHelper`] for more examples
155pub struct IfGetHelper {
156  values: Arc<RwLock<HashMap<String, Value>>>,
157}
158
159impl IfGetHelper {
160  pub fn new(values: &Arc<RwLock<HashMap<String, Value>>>) -> Self {
161    Self { values: values.clone() }
162  }
163}
164
165impl HelperDef for IfGetHelper {
166  fn call<'reg: 'rc, 'rc>(
167    &self,
168    h: &handlebars::Helper<'reg, 'rc>,
169    handle: &'reg handlebars::Handlebars<'reg>,
170    ctx: &'rc handlebars::Context,
171    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
172    out: &mut dyn handlebars::Output,
173  ) -> handlebars::HelperResult {
174    h.ensure_arguments_count(1, IF_SET_HELPER)?;
175    let key = h.get_param_as_str_or_fail(0, IF_SET_HELPER)?.to_string();
176    let has_value = has_value(&self.values, &key, IF_SET_HELPER)?;
177    let temp = if has_value { h.template() } else { h.inverse() };
178    if let Some(t) = temp {
179      t.render(handle, ctx, render_ctx, out)?
180    };
181    Ok(())
182  }
183}
184
185/// Clear a value in the key/value store
186///```
187/// # use codegenr_lib::helpers::*;
188/// # use serde_json::json;
189/// assert_eq!(
190///   exec_template(json!({}), r#"{{set "k" 42}}{{clear "k"}}{{#if_set "k"}}OK{{else}}NOK{{/if_set}}"#),
191///   "NOK"
192/// );
193/// assert_eq!(
194///   exec_template(json!({}), r#"{{clear "k"}}{{#if_set "k"}}OK{{else}}NOK{{/if_set}}"#),
195///   "NOK"
196/// );
197///```
198/// see [`GetHelper`] for more examples
199pub struct ClearHelper {
200  values: Arc<RwLock<HashMap<String, Value>>>,
201}
202
203impl ClearHelper {
204  pub fn new(values: &Arc<RwLock<HashMap<String, Value>>>) -> Self {
205    Self { values: values.clone() }
206  }
207}
208
209impl HelperDef for ClearHelper {
210  fn call<'reg: 'rc, 'rc>(
211    &self,
212    h: &handlebars::Helper<'reg, 'rc>,
213    _: &'reg handlebars::Handlebars<'reg>,
214    _: &'rc handlebars::Context,
215    _: &mut handlebars::RenderContext<'reg, 'rc>,
216    _: &mut dyn handlebars::Output,
217  ) -> handlebars::HelperResult {
218    h.ensure_arguments_count(1, GET_HELPER)?;
219    let key = h.get_param_as_str_or_fail(0, CLEAR_HELPER)?.to_string();
220    rem_value(&self.values, &key, CLEAR_HELPER)?;
221    Ok(())
222  }
223}
224
225// =============================================================================================
226// =============================================================================================
227
228fn get_value(values: &Arc<RwLock<HashMap<String, Value>>>, key: &str, helper_name: &str) -> Result<Option<Value>, RenderError> {
229  let lock = values
230    .read()
231    .map_err(|_e| RenderError::new(format!("Could not acquire lock in `{}` helper", helper_name)))?;
232  Ok(lock.get(key).map(Clone::clone))
233}
234
235fn has_value(values: &Arc<RwLock<HashMap<String, Value>>>, key: &str, helper_name: &str) -> Result<bool, RenderError> {
236  let lock = values
237    .read()
238    .map_err(|_e| RenderError::new(format!("Could not acquire lock in `{}` helper", helper_name)))?;
239  Ok(lock.get(key).is_some())
240}
241
242fn set_value(values: &Arc<RwLock<HashMap<String, Value>>>, key: String, value: Value, helper_name: &str) -> Result<(), RenderError> {
243  let mut lock = values
244    .write()
245    .map_err(|_| RenderError::new(format!("Could not acquire lock in `{}` helper", helper_name)))?;
246  lock.insert(key, value);
247  Ok(())
248}
249
250fn rem_value(values: &Arc<RwLock<HashMap<String, Value>>>, key: &str, helper_name: &str) -> Result<(), RenderError> {
251  let mut lock = values
252    .write()
253    .map_err(|_| RenderError::new(format!("Could not acquire lock in `{}` helper", helper_name)))?;
254  lock.remove(key);
255  Ok(())
256}