codegenr_lib/helpers/
strings.rs

1use crate::helpers::handlebars_ext::HandlebarsExt;
2use crate::helpers::string_ext::StringExt;
3use handlebars::{BlockContext, HelperDef, RenderError, Renderable, StringOutput};
4use serde_json::Value;
5
6pub const TRIM_HELPER: &str = "trim";
7pub const SPLIT_GET_FIRST_HELPER: &str = "split_get_first";
8pub const SPLIT_GET_LAST_HELPER: &str = "split_get_last";
9pub const TRIM_START_HELPER: &str = "trim_start";
10pub const TRIM_END_HELPER: &str = "trim_end";
11pub const START_WITH_HELPER: &str = "start_with";
12pub const WITH_MATCHING_HELPER: &str = "with_matching";
13pub const IF_ARRAY_CONTAINS: &str = "if_array_contains";
14pub const EACH_WITH_SORT_HELPER: &str = "each_with_sort";
15pub const TRIM_BLOCK_HELPER: &str = "trim_block";
16pub const TRIM_BLOCK_START_HELPER: &str = "trim_block_start";
17pub const TRIM_BLOCK_END_HELPER: &str = "trim_block_end";
18pub const ONE_LINE_HELPER: &str = "one_line";
19pub const NO_EMPTY_LINES_HELPER: &str = "no_empty_lines";
20/// Returns a string slice with leading and trailing whitespace removed.
21/// ```
22/// # use codegenr_lib::helpers::*;
23/// # use serde_json::json;
24/// assert_eq!(
25///   exec_template(json!({ "value": " test " }), "{{trim value}}"),
26///   "test"
27/// );
28/// assert_eq!(
29///   exec_template(json!({ "value": "-test-" }), "{{trim value \"-\"}}"),
30///   "test"
31/// );
32/// ```
33pub struct TrimHelper;
34
35impl HelperDef for TrimHelper {
36  fn call_inner<'reg: 'rc, 'rc>(
37    &self,
38    h: &handlebars::Helper<'reg, 'rc>,
39    _: &'reg handlebars::Handlebars<'reg>,
40    _: &'rc handlebars::Context,
41    _: &mut handlebars::RenderContext<'reg, 'rc>,
42  ) -> Result<handlebars::ScopedJson<'reg, 'rc>, handlebars::RenderError> {
43    h.ensure_arguments_count_min(1, TRIM_HELPER)?;
44    h.ensure_arguments_count_max(2, TRIM_HELPER)?;
45
46    let to_trim = h.get_param_as_str_or_fail(0, TRIM_HELPER)?.to_string();
47    let trimmer = h.get_param_as_str(1).map(|s| s.to_string());
48
49    Ok(Value::String(to_trim.trim_char(trimmer)).into())
50  }
51}
52
53/// Return the first part of a String splited by a definable parameter ('/' by default)
54///
55/// ```
56/// # use codegenr_lib::helpers::*;
57/// # use serde_json::json;
58/// assert_eq!(
59///   exec_template(json!({ "temp": "test/value" }), "{{split_get_first temp}}"),
60///   "test"
61/// );
62///  assert_eq!(
63///   exec_template(json!({ "temp": "-test-123-" }), "{{split_get_first temp \"-\"}}"),
64///   "test"
65/// );
66///
67/// ```
68pub struct SplitGetFirstHelper;
69
70impl HelperDef for SplitGetFirstHelper {
71  fn call_inner<'reg: 'rc, 'rc>(
72    &self,
73    h: &handlebars::Helper<'reg, 'rc>,
74    _: &'reg handlebars::Handlebars<'reg>,
75    _: &'rc handlebars::Context,
76    _: &mut handlebars::RenderContext<'reg, 'rc>,
77  ) -> Result<handlebars::ScopedJson<'reg, 'rc>, handlebars::RenderError> {
78    h.ensure_arguments_count_min(1, SPLIT_GET_FIRST_HELPER)?;
79    h.ensure_arguments_count_max(2, SPLIT_GET_FIRST_HELPER)?;
80
81    let to_split = h.get_param_as_str_or_fail(0, SPLIT_GET_FIRST_HELPER)?;
82    let splitter = h.get_param_as_str(1).map(|s| s.to_string());
83
84    Ok(handlebars::ScopedJson::Derived(Value::String(to_split.split_get_first(splitter))))
85  }
86}
87
88/// Return the last value of a String splited by a definable parameter ('/' by default)
89///
90/// ```
91/// # use codegenr_lib::helpers::*;
92/// # use serde_json::json;
93/// assert_eq!(
94///   exec_template(json!({ "temp": "test/value" }), "{{split_get_last temp}}"),
95///   "value"
96/// );
97///  assert_eq!(
98///   exec_template(json!({ "temp": "-test-123-" }), "{{split_get_last temp \"-\"}}"),
99///   "123"
100/// );
101/// ```
102pub struct SplitGetLastHelper;
103
104impl HelperDef for SplitGetLastHelper {
105  fn call_inner<'reg: 'rc, 'rc>(
106    &self,
107    h: &handlebars::Helper<'reg, 'rc>,
108    _: &'reg handlebars::Handlebars<'reg>,
109    _: &'rc handlebars::Context,
110    _: &mut handlebars::RenderContext<'reg, 'rc>,
111  ) -> Result<handlebars::ScopedJson<'reg, 'rc>, handlebars::RenderError> {
112    h.ensure_arguments_count_min(1, SPLIT_GET_LAST_HELPER)?;
113    h.ensure_arguments_count_max(2, SPLIT_GET_LAST_HELPER)?;
114
115    let to_split = h.get_param_as_str_or_fail(0, SPLIT_GET_LAST_HELPER)?;
116    let splitter = h.get_param_as_str(1).map(|s| s.to_string());
117
118    Ok(handlebars::ScopedJson::Derived(Value::String(to_split.split_get_last(splitter))))
119  }
120}
121
122/// Return a string trim only at the beggining by a definable parameter (' ' by default)
123///
124/// ```
125/// # use codegenr_lib::helpers::*;
126/// # use serde_json::json;
127/// assert_eq!(
128///   exec_template(json!({ "temp": " test " }), "{{trim_start temp}}"),
129///   "test "
130/// );
131/// assert_eq!(
132///   exec_template(json!({ "temp": "/test/" }), "{{trim_start temp \"/\"}}"),
133///   "test/"
134/// );
135/// ```
136pub struct TrimStartHelper;
137
138impl HelperDef for TrimStartHelper {
139  fn call_inner<'reg: 'rc, 'rc>(
140    &self,
141    h: &handlebars::Helper<'reg, 'rc>,
142    _: &'reg handlebars::Handlebars<'reg>,
143    _: &'rc handlebars::Context,
144    _: &mut handlebars::RenderContext<'reg, 'rc>,
145  ) -> Result<handlebars::ScopedJson<'reg, 'rc>, handlebars::RenderError> {
146    h.ensure_arguments_count_min(1, TRIM_START_HELPER)?;
147    h.ensure_arguments_count_max(2, TRIM_START_HELPER)?;
148
149    let to_trim = h.get_param_as_str_or_fail(0, TRIM_START_HELPER)?;
150    let splitter = h.get_param_as_str(1).map(|s| s.to_string());
151    Ok(handlebars::ScopedJson::Derived(Value::String(to_trim.trim_start_char(splitter))))
152  }
153}
154
155/// Return a string trim only at the end by a definable parameter (' ' by default)
156///
157/// ```
158/// # use codegenr_lib::helpers::*;
159/// # use serde_json::json;
160/// assert_eq!(
161///   exec_template(json!({ "temp": " test " }), "{{trim_end temp}}"),
162///   " test"
163/// );
164/// assert_eq!(
165///   exec_template(json!({ "temp": "/test/" }), "{{trim_end temp \"/\"}}"),
166///   "/test"
167/// );
168/// ```
169pub struct TrimEndHelper;
170
171impl HelperDef for TrimEndHelper {
172  fn call_inner<'reg: 'rc, 'rc>(
173    &self,
174    h: &handlebars::Helper<'reg, 'rc>,
175    _: &'reg handlebars::Handlebars<'reg>,
176    _: &'rc handlebars::Context,
177    _: &mut handlebars::RenderContext<'reg, 'rc>,
178  ) -> Result<handlebars::ScopedJson<'reg, 'rc>, handlebars::RenderError> {
179    h.ensure_arguments_count_min(1, TRIM_START_HELPER)?;
180    h.ensure_arguments_count_max(2, TRIM_START_HELPER)?;
181
182    let to_trim = h.get_param_as_str_or_fail(0, TRIM_END_HELPER)?;
183    let splitter = h.get_param_as_str(1).map(|s| s.to_string());
184    Ok(handlebars::ScopedJson::Derived(Value::String(to_trim.trim_end_char(splitter))))
185  }
186}
187
188/// Determines whether the beginning of the second argumentmatches the second one
189///```
190/// # use codegenr_lib::helpers::*;
191/// # use serde_json::json;
192/// assert_eq!(
193///   exec_template(json!({"one": "test-one", "two": "one-test"}), r#"{{#start_with "test" one}}OK{{else}}{{/start_with}}"#),
194///   "OK"
195/// );
196/// assert_eq!(
197///   exec_template(json!({"one": "test-one", "two": "one-test"}), r#"{{#start_with "test" two}}OK{{else}}NOK{{/start_with}}"#),
198///   "NOK"
199/// );
200///```
201pub struct StartWithHelper;
202
203impl HelperDef for StartWithHelper {
204  fn call<'reg: 'rc, 'rc>(
205    &self,
206    h: &handlebars::Helper<'reg, 'rc>,
207    handle: &'reg handlebars::Handlebars<'reg>,
208    ctx: &'rc handlebars::Context,
209    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
210    out: &mut dyn handlebars::Output,
211  ) -> handlebars::HelperResult {
212    h.ensure_arguments_count(2, START_WITH_HELPER)?;
213    let start = h.get_param_as_str_or_fail(0, START_WITH_HELPER)?;
214    let with = h.get_param_as_str_or_fail(1, START_WITH_HELPER)?;
215
216    let temp = if with.starts_with(start) { h.template() } else { h.inverse() };
217    if let Some(t) = temp {
218      t.render(handle, ctx, render_ctx, out)?
219    };
220    Ok(())
221  }
222}
223
224/// Execute the inner template with the matching parameter, when matching key is equal to the first parameter
225/// {{#with_matching some_value matching_key1 context1 mateching_key2 context2 ... }}
226/// Render the inverse template if no matching key was found
227///```
228/// # use codegenr_lib::helpers::*;
229/// # use serde_json::json;
230///
231/// assert_eq!(
232///   exec_template(json!({}), r#"{{#with_matching "test" "1" "1" "2" "2"}}{{else}}NOT FOUND{{/with_matching}}"#),
233///   "NOT FOUND"
234/// );
235/// assert_eq!(
236///   exec_template(json!({}), r#"{{#with_matching "2" "1" "01" "2" "02"}}{{this}}{{else}}NOT FOUND{{/with_matching}}"#),
237///   "02"
238/// );
239/// assert_eq!(
240///   exec_template(json!({ "value": "42" }), r#"{{#with_matching value "42" "toto"}}{{this}}{{else}}NOT FOUND{{/with_matching}}"#),
241///   "toto"
242/// );
243/// assert_eq!(
244///   exec_template(json!({ "value": "42" }), r#"{{#with_matching value "43" "toto"}}{{this}}{{else}}NOT FOUND{{/with_matching}}"#),
245///   "NOT FOUND"
246/// );
247/// assert_eq!(
248///   exec_template(json!({ "value": "42" }), r#"{{#with_matching value "42" "toto"}}{{this}}{{else}}NOT FOUND{{/with_matching}}_and_{{value}}"#),
249///   "toto_and_42"
250/// );
251///```
252pub struct WithMatchingHelper;
253
254impl HelperDef for WithMatchingHelper {
255  fn call<'reg: 'rc, 'rc>(
256    &self,
257    h: &handlebars::Helper<'reg, 'rc>,
258    handle: &'reg handlebars::Handlebars<'reg>,
259    ctx: &'rc handlebars::Context,
260    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
261    out: &mut dyn handlebars::Output,
262  ) -> handlebars::HelperResult {
263    h.ensure_arguments_count_min(3, WITH_MATCHING_HELPER)?;
264    let arguments_count = h.params().len();
265    if arguments_count % 2 != 1 {
266      return Err(RenderError::new(format!(
267        "Arguments number for the `{}` helper must be an odd number.",
268        WITH_MATCHING_HELPER
269      )));
270    }
271
272    let key = h.get_param_as_json_or_fail(0, WITH_MATCHING_HELPER)?;
273
274    let mut pair_position = 1;
275    while pair_position < arguments_count {
276      let match_key = h.get_param_as_json_or_fail(pair_position, WITH_MATCHING_HELPER)?;
277      // todo: for strings, be case insensitive : value.to_lowercase() == match_key.unwrap().to_lowercase()
278      if key == match_key {
279        if let Some(t) = h.template() {
280          let match_value = h.get_param_as_json_or_fail(pair_position + 1, WITH_MATCHING_HELPER)?;
281          let mut block = BlockContext::new();
282          block.set_base_value(match_value.clone());
283          render_ctx.push_block(block);
284          t.render(handle, ctx, render_ctx, out)?;
285          render_ctx.pop_block();
286        };
287        return Ok(());
288      }
289      pair_position += 2;
290    }
291
292    if let Some(t) = h.inverse() {
293      t.render(handle, ctx, render_ctx, out)?
294    };
295    Ok(())
296  }
297}
298
299/// Write the template if the second argument is found in the array passed as first argument
300/// (values are compared with string insensitive comparison)
301/// (Pas completement fonctionnelle)
302///```
303/// # use codegenr_lib::helpers::*;
304/// # use serde_json::json;
305/// let json_array = json!({ "type": "object", "required": [ "errorMeSSage", "test" ], "properties": {"errorMessage": {"type": "string"}, "non_required_prop" : {"type" : "int"}}});
306/// assert_eq!(
307///   exec_template(json_array.clone(), r#"{{#if_array_contains required "errorMeSSage"}}OK{{else}}NOK{{/if_array_contains}}"#),
308///   "OK"
309/// );
310/// //assert_eq!(
311///   //exec_template(json_array.clone(), r#"{{#if_array_contains required "errormessage"}}OK{{else}}NOK{{/if_array_contains}}"#),
312///   //"OK"
313/// //);
314/// assert_eq!(
315///   exec_template(json_array.clone(), r#"{{#if_array_contains required "test"}}OK{{else}}NOK{{/if_array_contains}}"#),
316///   "OK"
317/// );
318/// assert_eq!(
319///   exec_template(json_array.clone(), r#"{{#if_array_contains required "notFound"}}OK{{else}}NOK{{/if_array_contains}}"#),
320///   "NOK"
321/// );
322///```
323pub struct IfArrayContainsHelper;
324
325impl HelperDef for IfArrayContainsHelper {
326  fn call<'reg: 'rc, 'rc>(
327    &self,
328    h: &handlebars::Helper<'reg, 'rc>,
329    handle: &'reg handlebars::Handlebars<'reg>,
330    ctx: &'rc handlebars::Context,
331    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
332    out: &mut dyn handlebars::Output,
333  ) -> handlebars::HelperResult {
334    h.ensure_arguments_count(2, IF_ARRAY_CONTAINS)?;
335    let value = h.get_param_as_array_or_fail(0, IF_ARRAY_CONTAINS)?;
336    let key = h.get_param_as_json_or_fail(1, IF_ARRAY_CONTAINS)?;
337
338    // todo: compare case insensitive when both strings
339    let is_value_found = value.iter().any(|s| s == key);
340    let temp = if is_value_found { h.template() } else { h.inverse() };
341
342    if let Some(t) = temp {
343      t.render(handle, ctx, render_ctx, out)?
344    };
345    Ok(())
346  }
347}
348
349/// Trim start and end of a block output
350/// (all arguments are converted to string and case insensitive compared)
351///```
352/// # use codegenr_lib::helpers::*;
353/// # use serde_json::json;
354/// assert_eq!(
355///   exec_template(json!({}), r#"{{#trim_block " "}} 1,2,3,4 {{/trim_block}}"#),
356///   "1,2,3,4"
357/// );
358/// assert_eq!(
359///   exec_template(json!({}), r#"{{#trim_block ","}}1,2,3,4{{/trim_block}}"#),
360///   "1,2,3,4"
361/// );
362/// assert_eq!(
363///   exec_template(json!({}), r#"{{#trim_block ","}}1,2,3,4,{{/trim_block}}"#),
364///   "1,2,3,4"
365/// );
366/// assert_eq!(
367///   exec_template(json!({}), r#"{{#trim_block ","}},1,2,3,4,{{/trim_block}}"#),
368///   "1,2,3,4"
369/// );
370/// assert_eq!(
371///   exec_template(json!({}), r#"{{#trim_block ","}},,1,2,3,4,,{{/trim_block}}"#),
372///   "1,2,3,4"
373/// );
374/// assert_eq!(
375///   exec_template(json!({"a": "42", "b": "42", "c": "42"}), r#"{{#trim_block ","}}{{#each this}}{{@key}},{{/each}}{{/trim_block}}"#),
376///   "a,b,c"
377/// );
378///```
379pub struct TrimBlockHelper;
380
381impl HelperDef for TrimBlockHelper {
382  fn call<'reg: 'rc, 'rc>(
383    &self,
384    h: &handlebars::Helper<'reg, 'rc>,
385    handle: &'reg handlebars::Handlebars<'reg>,
386    ctx: &'rc handlebars::Context,
387    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
388    out: &mut dyn handlebars::Output,
389  ) -> handlebars::HelperResult {
390    if let Some(t) = h.template() {
391      let mut buffer = StringOutput::new();
392      t.render(handle, ctx, render_ctx, &mut buffer)?;
393      let s = buffer.into_string()?;
394      let trimmer = h.get_param_as_str(0).map(|s| s.to_string());
395
396      out.write(&s.trim_char(trimmer))?;
397    };
398
399    Ok(())
400  }
401}
402
403/// Trim start of a block output
404/// (all arguments are converted to string and case insensitive compared)
405///```
406/// # use codegenr_lib::helpers::*;
407/// # use serde_json::json;
408/// assert_eq!(
409///   exec_template(json!({}), r#"{{#trim_block_start}} 1,2,3,4 {{/trim_block_start}}"#),
410///   "1,2,3,4 "
411/// );
412/// assert_eq!(
413///   exec_template(json!({}), r#"{{#trim_block_start ","}}1,2,3,4{{/trim_block_start}}"#),
414///   "1,2,3,4"
415/// );
416/// assert_eq!(
417///   exec_template(json!({}), r#"{{#trim_block_start ","}}1,2,3,4,{{/trim_block_start}}"#),
418///   "1,2,3,4,"
419/// );
420/// assert_eq!(
421///   exec_template(json!({}), r#"{{#trim_block_start ","}},1,2,3,4,{{/trim_block_start}}"#),
422///   "1,2,3,4,"
423/// );
424/// assert_eq!(
425///   exec_template(json!({}), r#"{{#trim_block_start ","}},,1,2,3,4,,{{/trim_block_start}}"#),
426///   "1,2,3,4,,"
427/// );
428/// assert_eq!(
429///   exec_template(json!({"a": "42", "b": "42", "c": "42"}), r#"{{#trim_block_start ","}}{{#each this}}{{@key}},{{/each}}{{/trim_block_start}}"#),
430///   "a,b,c,"
431/// );
432///```
433pub struct TrimBlockStartHelper;
434
435impl HelperDef for TrimBlockStartHelper {
436  fn call<'reg: 'rc, 'rc>(
437    &self,
438    h: &handlebars::Helper<'reg, 'rc>,
439    handle: &'reg handlebars::Handlebars<'reg>,
440    ctx: &'rc handlebars::Context,
441    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
442    out: &mut dyn handlebars::Output,
443  ) -> handlebars::HelperResult {
444    if let Some(t) = h.template() {
445      let mut buffer = StringOutput::new();
446      t.render(handle, ctx, render_ctx, &mut buffer)?;
447      let s = buffer.into_string()?;
448      let trimmer = h.get_param_as_str(0).map(|s| s.to_string());
449
450      out.write(&s.trim_start_char(trimmer))?;
451    };
452
453    Ok(())
454  }
455}
456
457/// Trim end of a block output
458/// (all arguments are converted to string and case insensitive compared)
459///```
460/// # use codegenr_lib::helpers::*;
461/// # use serde_json::json;
462/// assert_eq!(
463///   exec_template(json!({}), r#"{{#trim_block_end " "}} 1,2,3,4 {{/trim_block_end}}"#),
464///   " 1,2,3,4"
465/// );
466/// assert_eq!(
467///   exec_template(json!({}), r#"{{#trim_block_end ","}}1,2,3,4{{/trim_block_end}}"#),
468///   "1,2,3,4"
469/// );
470/// assert_eq!(
471///   exec_template(json!({}), r#"{{#trim_block_end ","}}1,2,3,4,{{/trim_block_end}}"#),
472///   "1,2,3,4"
473/// );
474/// assert_eq!(
475///   exec_template(json!({}), r#"{{#trim_block_end ","}},1,2,3,4,{{/trim_block_end}}"#),
476///   ",1,2,3,4"
477/// );
478/// assert_eq!(
479///   exec_template(json!({}), r#"{{#trim_block_end ","}},,1,2,3,4,,{{/trim_block_end}}"#),
480///   ",,1,2,3,4"
481/// );
482/// assert_eq!(
483///   exec_template(json!({"a": "42", "b": "42", "c": "42"}), r#"{{#trim_block_end ","}}{{#each this}}{{@key}},{{/each}}{{/trim_block_end}}"#),
484///   "a,b,c"
485/// );
486///```
487pub struct TrimBlockEndHelper;
488
489impl HelperDef for TrimBlockEndHelper {
490  fn call<'reg: 'rc, 'rc>(
491    &self,
492    h: &handlebars::Helper<'reg, 'rc>,
493    handle: &'reg handlebars::Handlebars<'reg>,
494    ctx: &'rc handlebars::Context,
495    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
496    out: &mut dyn handlebars::Output,
497  ) -> handlebars::HelperResult {
498    if let Some(t) = h.template() {
499      let mut buffer = StringOutput::new();
500      t.render(handle, ctx, render_ctx, &mut buffer)?;
501      let s = buffer.into_string()?;
502      let trimmer = h.get_param_as_str(0).map(|s| s.to_string());
503      out.write(&s.trim_end_char(trimmer))?;
504    };
505
506    Ok(())
507  }
508}
509
510/// Trim end of a block output
511/// (all arguments are converted to string and case insensitive compared)
512///```
513/// # use codegenr_lib::helpers::*;
514/// # use serde_json::json;
515/// assert_eq!(
516///   exec_template(json!({}), "{{#one_line}} {{/one_line}}"),
517///   "\n"
518/// );
519/// assert_eq!(
520///   exec_template(json!({}), "{{#one_line}} |do not < remove please >| {{/one_line}}"),
521///   "|do not < remove please >|\n"
522/// );
523/// assert_eq!(
524///   exec_template(json!({}), "{{#one_line}} \n {{/one_line}}"),
525///   "\n"
526/// );
527/// assert_eq!(
528///   exec_template(json!({}), "{{#one_line}}\n {{/one_line}}"),
529///   "\n"
530/// );
531/// assert_eq!(
532///   exec_template(json!({}), "{{#one_line}}\n{{/one_line}}"),
533///   "\n"
534/// );
535/// assert_eq!(
536///   exec_template(json!({}), "{{#one_line}} \r\n {{/one_line}}"),
537///   "\n"
538/// );
539/// assert_eq!(
540///   exec_template(json!({}), "{{#one_line}}\r\n{{/one_line}}"),
541///   "\n"
542/// );
543/// assert_eq!(
544///   exec_template(json!({}), "{{#one_line}} test{{/one_line}}"),
545///   "test\n"
546/// );
547/// assert_eq!(
548///   exec_template(json!({}), "{{#one_line}} a \n z {{/one_line}}"),
549///   "az\n"
550/// );
551/// assert_eq!(
552///   exec_template(json!({}), "{{#one_line}}a\n z{{/one_line}}"),
553///   "az\n"
554/// );
555/// assert_eq!(
556///   exec_template(json!({}), "{{#one_line}}a\nz{{/one_line}}"),
557///   "az\n"
558/// );
559/// assert_eq!(
560///   exec_template(json!({}), "{{#one_line}}a \r\n z{{/one_line}}"),
561///   "az\n"
562/// );
563/// assert_eq!(
564///   exec_template(json!({}), "{{#one_line}}a \r\n \r\n \r\nz{{/one_line}}"),
565///   "az\n"
566/// );
567/// assert_eq!(
568///   exec_template(json!({}), "{{#one_line 2 true \"-\"}}a \r\n \r\n \r\nz{{/one_line}}"),
569///   "  a---z\n"
570/// );
571/// assert_eq!(
572///   exec_template(json!({}), "{{#one_line}}test\r\n\r\n\r\ntest{{/one_line}}"),
573///   "testtest\n"
574/// );
575/// assert_eq!(
576///   exec_template(json!({}), "{{#one_line 0 \"true\"}}test\r\n\r\n\r\ntest{{/one_line}}"),
577///  "testtest\n"
578/// );
579/// assert_eq!(
580///   exec_template(json!({}), "{{#one_line 0 false}}test\r\n\r\n\r\ntest{{/one_line}}"),
581///   "testtest"
582/// );
583/// assert_eq!(
584///   exec_template(json!({}), "{{#one_line}}{{/one_line}}"),
585///   "\n"
586/// );
587/// assert_eq!(
588///   exec_template(json!({}), "{{#one_line}}   test {{/one_line}}"),
589/// "test\n"
590/// );
591/// assert_eq!(
592///   exec_template(json!({}), "{{#one_line 5}}test{{/one_line}}"),
593/// "     test\n"
594/// );
595///```
596pub struct OneLineHelper;
597
598impl HelperDef for OneLineHelper {
599  fn call<'reg: 'rc, 'rc>(
600    &self,
601    h: &handlebars::Helper<'reg, 'rc>,
602    handle: &'reg handlebars::Handlebars<'reg>,
603    ctx: &'rc handlebars::Context,
604    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
605    out: &mut dyn handlebars::Output,
606  ) -> handlebars::HelperResult {
607    if let Some(t) = h.template() {
608      h.ensure_arguments_count_max(3, ONE_LINE_HELPER)?;
609      let mut buffer = StringOutput::new();
610      t.render(handle, ctx, render_ctx, &mut buffer)?;
611      let s = buffer.into_string()?;
612      let indent = h.get_param_as_integer(0);
613      let line_break = h.get_param_as_bool(1);
614      let replacer = h.get_param_as_str(2);
615      out.write(&s.on_one_line(indent, line_break, replacer))?;
616    };
617
618    Ok(())
619  }
620}
621
622/// Removes empty lines from the block
623///```
624/// # use codegenr_lib::helpers::*;
625/// # use serde_json::json;
626/// assert_eq!(
627///   exec_template(json!({}), "{{#no_empty_lines}}{{/no_empty_lines}}"),
628///   ""
629/// );
630/// assert_eq!(
631///   exec_template(json!({}), "{{#no_empty_lines}} {{/no_empty_lines}}"),
632///   "\n"
633/// );
634/// assert_eq!(
635///   exec_template(json!({}), "{{#no_empty_lines}}a\n \t \n b {{/no_empty_lines}}"),
636///   "a\n b \n"
637/// );
638/// assert_eq!(
639///   exec_template(json!({}), "{{#no_empty_lines}}\r\na\n \t \nb\r\nc\r\n\r\n{{/no_empty_lines}}"),
640///   "a\nb\nc\n"
641/// );
642///```
643pub struct NoEmptyLinesHelper;
644
645impl HelperDef for NoEmptyLinesHelper {
646  fn call<'reg: 'rc, 'rc>(
647    &self,
648    h: &handlebars::Helper<'reg, 'rc>,
649    handle: &'reg handlebars::Handlebars<'reg>,
650    ctx: &'rc handlebars::Context,
651    render_ctx: &mut handlebars::RenderContext<'reg, 'rc>,
652    out: &mut dyn handlebars::Output,
653  ) -> handlebars::HelperResult {
654    if let Some(t) = h.template() {
655      h.ensure_arguments_count_max(2, ONE_LINE_HELPER)?;
656      let mut buffer = StringOutput::new();
657      t.render(handle, ctx, render_ctx, &mut buffer)?;
658      let content = buffer.into_string()?;
659
660      let mut non_empty_lines_count: usize = 0;
661      let mut empty_lines_count: usize = 0;
662      for line in content.lines() {
663        let is_empty = line.trim().is_empty();
664        let (should_write_line, should_right_newline) = match (non_empty_lines_count, empty_lines_count, is_empty) {
665          (0, 0, true) => (false, true),
666          (_, _, true) => (false, false),
667          (_, _, false) => (true, true),
668        };
669        if should_write_line {
670          out.write(line)?;
671        }
672        if should_right_newline {
673          out.write("\n")?;
674        }
675        if is_empty {
676          non_empty_lines_count += 1;
677        } else {
678          empty_lines_count += 1;
679        }
680      }
681    };
682    Ok(())
683  }
684}
685
686///// Trim end of a block output
687///// (all arguments are converted to string and case insensitive compared)
688/////```
689///// # use codegenr_lib::helpers::*;
690///// # use serde_json::json;
691///// assert_eq!(
692/////   exec_template(json!([{"t": "c"}, {"t": "a"}, {"t": "b"}]), r#"{{#each this}}{{t}}{{/each}}"#),
693/////   "cab"
694///// );
695///// assert_eq!(
696/////   exec_template(json!([{"t": "c"}, {"t": "a"}, {"t": "b"}]), r#"{{#each_with_sort this "t/a/"}}{{t}}{{/each_with_sort}}"#),
697/////   "abc"
698///// );
699///// assert_eq!(
700/////   exec_template(json!([{t: 'c'}, {t: 'a'}, {t: 'b'}]), r#"{{#each_with_sort . 't'}}{{#each .}}{{t}}{{/each}}{{/each_with_sort}}"#),
701/////   "abc"
702///// );
703///// assert_eq!(
704/////   exec_template(json!({[]}), r#"{{#each_with_sort . .}}{{/each_with_sort}}"#),
705/////   ""
706///// );
707///// assert_eq!(
708/////   exec_template(json!({ a : {}, b : {} }), r#"{{#each_with_sort .}}{{#each .}}{{@key}}{{/each}}{{/each_with_sort}}"#),
709/////   "ab"
710///// );
711///// assert_eq!(
712/////   exec_template(json!({ b : {}, a : {} }), r#"{{#each_with_sort .}}{{#each .}}{{@key}}{{/each}}{{/each_with_sort}}"#),
713/////   "ab"
714///// );
715///// assert_eq!(
716/////   exec_template(json!({\r\n{\r\n "swagger": "2.0",\r\n "info": {\r\n "title": "Marketplace Gateway API - Feeds",\r\n ...), r#"{{#each_with_sort parameters}}{{#each .}}{{@key}},{{/each}}{{/each_with_sort}}"#),
717/////   "accountIdParameter,credentialParameter,feedTypeParameter,marketplaceBusinessCodeParameter,publicationIdParameter,"
718///// );
719/////```
720// #[derive(Clone, Copy)]
721// pub struct EachWithSortHelper;
722
723// /*
724
725//  FULL =  { data: { t: { a: "b" }, ttt: [ 42 ]  }}
726
727//   {{#each data}}    ScopedJson:Context (FULL, vec!())
728//     {{#with t}}     ScopedJson:Context (FULL, vec!("data/0"))
729//       {{../ttt}}    ScopedJson:Derived ({ a: "b" }, vec!())
730//     {{/with}}
731//   {{/each}}
732
733// */
734// impl HelperDef for EachWithSortHelper {
735//   fn call<'reg: 'rc, 'rc>(
736//     &self,
737//     h: &Helper<'reg, 'rc>,
738//     r: &'reg handlebars::Handlebars<'reg>,
739//     ctx: &'rc handlebars::Context,
740//     rc: &mut handlebars::RenderContext<'reg, 'rc>,
741//     out: &mut dyn handlebars::Output,
742//   ) -> handlebars::HelperResult {
743//     let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each\""))?;
744//     let j_path = h.get_param_as_str(1).unwrap_or("");
745
746//     let template = h.template();
747
748//     match template {
749//       Some(t) => match *value.value() {
750//         Value::Array(ref list) if !list.is_empty() || (list.is_empty() && h.inverse().is_none()) => {
751//           let mut to_sort = list.clone();
752
753//           to_sort.sort_by(|a, b| {
754//             todo!("Find a way !!");
755//             std::cmp::Ordering::Greater
756//           });
757
758//           let block_context = create_block(value);
759//           rc.push_block(block_context);
760
761//           let len = list.len();
762
763//           let array_path = value.context_path();
764
765//           for (i, v) in to_sort.iter().enumerate().take(len) {
766//             if let Some(ref mut block) = rc.block_mut() {
767//               let is_first = i == 0usize;
768//               let is_last = i == len - 1;
769
770//               let index = to_json(i);
771//               block.set_local_var("first", to_json(is_first));
772//               block.set_local_var("last", to_json(is_last));
773//               block.set_local_var("index", index.clone());
774
775//               update_block_context(block, array_path, i.to_string(), is_first, v);
776//               set_block_param(block, h, array_path, &index, v)?;
777//             }
778
779//             t.render(r, ctx, rc, out)?;
780//           }
781
782//           rc.pop_block();
783//           Ok(())
784//         }
785//         Value::Object(ref obj) if !obj.is_empty() || (obj.is_empty() && h.inverse().is_none()) => {
786//           let block_context = create_block(value);
787//           rc.push_block(block_context);
788
789//           let len = obj.len();
790
791//           let obj_path = value.context_path();
792
793//           for (i, (k, v)) in obj.iter().enumerate() {
794//             if let Some(ref mut block) = rc.block_mut() {
795//               let is_first = i == 0usize;
796//               let is_last = i == len - 1;
797
798//               let key = to_json(k);
799//               block.set_local_var("first", to_json(is_first));
800//               block.set_local_var("last", to_json(is_last));
801//               block.set_local_var("key", key.clone());
802
803//               update_block_context(block, obj_path, k.to_string(), is_first, v);
804//               set_block_param(block, h, obj_path, &key, v)?;
805//             }
806
807//             t.render(r, ctx, rc, out)?;
808//           }
809
810//           rc.pop_block();
811//           Ok(())
812//         }
813//         _ => {
814//           if let Some(else_template) = h.inverse() {
815//             else_template.render(r, ctx, rc, out)
816//           } else if r.strict_mode() {
817//             Err(RenderError::strict_error(value.relative_path()))
818//           } else {
819//             Ok(())
820//           }
821//         }
822//       },
823//       None => Ok(()),
824//     }
825//   }
826// }
827
828// fn update_block_context<'reg>(
829//   block: &mut BlockContext<'reg>,
830//   base_path: Option<&Vec<String>>,
831//   relative_path: String,
832//   is_first: bool,
833//   value: &Value,
834// ) {
835//   if let Some(p) = base_path {
836//     if is_first {
837//       *block.base_path_mut() = copy_on_push_vec(p, relative_path);
838//     } else if let Some(ptr) = block.base_path_mut().last_mut() {
839//       *ptr = relative_path;
840//     }
841//   } else {
842//     block.set_base_value(value.clone());
843//   }
844// }
845
846// fn set_block_param<'reg: 'rc, 'rc>(
847//   block: &mut BlockContext<'reg>,
848//   h: &Helper<'reg, 'rc>,
849//   base_path: Option<&Vec<String>>,
850//   k: &Value,
851//   v: &Value,
852// ) -> Result<(), RenderError> {
853//   if let Some(bp_val) = h.block_param() {
854//     let mut params = BlockParams::new();
855//     if base_path.is_some() {
856//       params.add_path(bp_val, Vec::with_capacity(0))?;
857//     } else {
858//       params.add_value(bp_val, v.clone())?;
859//     }
860
861//     block.set_block_params(params);
862//   } else if let Some((bp_val, bp_key)) = h.block_param_pair() {
863//     let mut params = BlockParams::new();
864//     if base_path.is_some() {
865//       params.add_path(bp_val, Vec::with_capacity(0))?;
866//     } else {
867//       params.add_value(bp_val, v.clone())?;
868//     }
869//     params.add_value(bp_key, k.clone())?;
870
871//     block.set_block_params(params);
872//   }
873
874//   Ok(())
875// }
876
877// pub fn create_block<'reg: 'rc, 'rc>(param: &'rc PathAndJson<'reg, 'rc>) -> BlockContext<'reg> {
878//   let mut block = BlockContext::new();
879
880//   if let Some(new_path) = param.context_path() {
881//     *block.base_path_mut() = new_path.clone();
882//   } else {
883//     // use clone for now
884//     block.set_base_value(param.value().clone());
885//   }
886
887//   block
888// }
889
890// fn copy_on_push_vec<T>(input: &[T], el: T) -> Vec<T>
891// where
892//   T: Clone,
893// {
894//   let mut new_vec = Vec::with_capacity(input.len() + 1);
895//   new_vec.extend_from_slice(input);
896//   new_vec.push(el);
897//   new_vec
898// }