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// }