handlebars_misc_helpers/
string_helpers.rs

1use cruet::Inflector;
2use handlebars::{
3    handlebars_helper, Context, Handlebars, Helper, HelperDef, RenderContext, RenderError,
4    ScopedJson,
5};
6
7#[macro_export]
8macro_rules! handlebars_register_cruet {
9    ($engine:ident, $fct_name:ident) => {
10        {
11            handlebars_helper!($fct_name: |v: str| v.$fct_name());
12            $engine.register_helper(stringify!($fct_name), Box::new($fct_name))
13        }
14    }
15}
16
17#[allow(non_camel_case_types)]
18pub struct first_non_empty_fct;
19
20impl HelperDef for first_non_empty_fct {
21    fn call_inner<'reg: 'rc, 'rc>(
22        &self,
23        h: &Helper<'rc>,
24        _: &'reg Handlebars,
25        _: &'rc Context,
26        _: &mut RenderContext<'reg, 'rc>,
27    ) -> Result<ScopedJson<'reg>, RenderError> {
28        let params = h.params();
29        Ok(params
30            .iter()
31            .filter_map(|p| p.value().as_str().filter(|s| !s.is_empty()))
32            // .filter_map(|p| {
33            //     serde_json::to_string(p.value())
34            //         .ok()
35            //         .filter(|s| !s.is_empty())
36            // })
37            .next()
38            .map(|v| ScopedJson::Derived(serde_json::Value::String(v.to_owned())))
39            .unwrap_or_else(|| ScopedJson::Derived(serde_json::Value::String("".to_owned()))))
40    }
41}
42
43pub fn register(handlebars: &mut Handlebars) {
44    {
45        handlebars_helper!(to_lower_case: |v: str| v.to_lowercase());
46        handlebars.register_helper("to_lower_case", Box::new(to_lower_case))
47    }
48    {
49        handlebars_helper!(to_upper_case: |v: str| v.to_uppercase());
50        handlebars.register_helper("to_upper_case", Box::new(to_upper_case))
51    }
52    {
53        handlebars_helper!(trim: |v: str| v.trim());
54        handlebars.register_helper("trim", Box::new(trim))
55    }
56    {
57        handlebars_helper!(trim_start: |v: str| v.trim_start());
58        handlebars.register_helper("trim_start", Box::new(trim_start))
59    }
60    {
61        handlebars_helper!(trim_end: |v: str| v.trim_end());
62        handlebars.register_helper("trim_end", Box::new(trim_end))
63    }
64    {
65        handlebars_helper!(replace: |v: str, from: str, to: str| v.replace(from, to));
66        handlebars.register_helper("replace", Box::new(replace))
67    }
68    handlebars_register_cruet!(handlebars, is_class_case);
69    handlebars_register_cruet!(handlebars, to_class_case);
70    handlebars_register_cruet!(handlebars, is_camel_case);
71    handlebars_register_cruet!(handlebars, to_camel_case);
72    handlebars_register_cruet!(handlebars, is_pascal_case);
73    handlebars_register_cruet!(handlebars, to_pascal_case);
74    handlebars_register_cruet!(handlebars, is_snake_case);
75    handlebars_register_cruet!(handlebars, to_snake_case);
76    handlebars_register_cruet!(handlebars, is_screaming_snake_case);
77    handlebars_register_cruet!(handlebars, to_screaming_snake_case);
78    handlebars_register_cruet!(handlebars, is_kebab_case);
79    handlebars_register_cruet!(handlebars, to_kebab_case);
80    handlebars_register_cruet!(handlebars, is_train_case);
81    handlebars_register_cruet!(handlebars, to_train_case);
82    handlebars_register_cruet!(handlebars, is_sentence_case);
83    handlebars_register_cruet!(handlebars, to_sentence_case);
84    handlebars_register_cruet!(handlebars, is_title_case);
85    handlebars_register_cruet!(handlebars, to_title_case);
86    handlebars_register_cruet!(handlebars, is_table_case);
87    handlebars_register_cruet!(handlebars, to_table_case);
88    handlebars_register_cruet!(handlebars, deordinalize);
89    handlebars_register_cruet!(handlebars, ordinalize);
90    handlebars_register_cruet!(handlebars, is_foreign_key);
91    handlebars_register_cruet!(handlebars, to_foreign_key);
92    handlebars_register_cruet!(handlebars, deconstantize);
93    handlebars_register_cruet!(handlebars, demodulize);
94    handlebars_register_cruet!(handlebars, to_plural);
95    handlebars_register_cruet!(handlebars, to_singular);
96    {
97        handlebars_helper!(quote: |quote_symbol: str, v: str| enquote::enquote(quote_symbol.chars().next().unwrap_or('"'), v));
98        handlebars.register_helper("quote", Box::new(quote))
99    }
100    {
101        handlebars_helper!(unquote: |v: str| match enquote::unquote(v){
102            Err(e) => {
103                log::warn!(
104                    "helper: unquote failed for string '{:?}' with error '{:?}'",
105                    v, e
106                );
107                v.to_owned()
108            }
109            Ok(s) => s,
110        });
111        handlebars.register_helper("unquote", Box::new(unquote))
112    }
113    handlebars.register_helper("first_non_empty", Box::new(first_non_empty_fct));
114}
115
116#[cfg(test)]
117mod tests {
118    use crate::assert_renders;
119    use crate::tests::assert_helpers;
120    use std::error::Error;
121
122    #[test]
123    fn test_register_string_helpers() -> Result<(), Box<dyn Error>> {
124        assert_helpers(
125            "Hello foo-bars",
126            vec![
127                ("to_lower_case", "hello foo-bars"),
128                ("to_upper_case", "HELLO FOO-BARS"),
129                ("to_camel_case", "helloFooBars"),
130                ("to_pascal_case", "HelloFooBars"),
131                ("to_snake_case", "hello_foo_bars"),
132                ("to_screaming_snake_case", "HELLO_FOO_BARS"),
133                ("to_kebab_case", "hello-foo-bars"),
134                ("to_train_case", "Hello-Foo-Bars"),
135                ("to_sentence_case", "Hello foo bars"),
136                ("to_title_case", "Hello Foo Bars"),
137                ("to_class_case", "HelloFooBar"),
138                ("to_table_case", "hello_foo_bars"),
139                ("to_plural", "bars"),
140                ("to_singular", "bar"),
141            ],
142        )?;
143        Ok(())
144    }
145
146    #[test]
147    fn test_helper_trim() -> Result<(), Box<dyn Error>> {
148        assert_renders![
149            (r##"{{ trim "foo" }}"##, r##"foo"##),
150            (r##"{{ trim "  foo" }}"##, r##"foo"##),
151            (r##"{{ trim "foo  " }}"##, r##"foo"##),
152            (r##"{{ trim " foo " }}"##, r##"foo"##)
153        ]
154    }
155
156    #[test]
157    fn test_helper_trim_start() -> Result<(), Box<dyn Error>> {
158        assert_renders![
159            (r##"{{ trim_start "foo" }}"##, r##"foo"##),
160            (r##"{{ trim_start "  foo" }}"##, r##"foo"##),
161            (r##"{{ trim_start "foo  " }}"##, r##"foo  "##),
162            (r##"{{ trim_start " foo " }}"##, r##"foo "##)
163        ]
164    }
165
166    #[test]
167    fn test_helper_trim_end() -> Result<(), Box<dyn Error>> {
168        assert_renders![
169            (r##"{{ trim_end "foo" }}"##, r##"foo"##),
170            (r##"{{ trim_end "  foo" }}"##, r##"  foo"##),
171            (r##"{{ trim_end "foo  " }}"##, r##"foo"##),
172            (r##"{{ trim_end " foo " }}"##, r##" foo"##)
173        ]
174    }
175
176    #[test]
177    fn test_helper_quote() -> Result<(), Box<dyn Error>> {
178        assert_renders![
179            (r##"{{ quote "'" "''" }}"##, r##"'\'\''"##),
180            (r##"{{ quote "'" "foo" }}"##, r##"'foo'"##),
181            (r##"{{ quote "\"" "foo" }}"##, r##""foo""##),
182            (r##"{{ quote "" "foo" }}"##, r##""foo""##),
183        ]
184    }
185
186    #[test]
187    fn test_helper_unquote() -> Result<(), Box<dyn Error>> {
188        assert_renders![
189            (r##"{{ unquote "''" }}"##, r##""##),
190            (r##"{{ unquote "'f'" }}"##, r##"f"##),
191            (r##"{{ unquote "foo" }}"##, r##"foo"##),
192            (r##"{{ unquote "'foo'" }}"##, r##"foo"##),
193            (r##"{{ unquote "\"foo\"" }}"##, r##"foo"##),
194            (r##"{{ unquote "foo'" }}"##, r##"foo'"##),
195            (r##"{{ unquote "'foo" }}"##, r##"'foo"##),
196        ]
197    }
198
199    #[test]
200    fn test_helper_replace() -> Result<(), Box<dyn Error>> {
201        assert_renders![(r##"{{ replace "foo" "oo" "aa"}}"##, r##"faa"##)]
202    }
203
204    #[test]
205    fn test_helper_first_non_empty() -> Result<(), Box<dyn Error>> {
206        assert_renders![
207            (r##"{{ first_non_empty ""}}"##, r##""##),
208            (r##"{{ first_non_empty "foo"}}"##, r##"foo"##),
209            (r##"{{ first_non_empty "foo" "bar"}}"##, r##"foo"##),
210            (r##"{{ first_non_empty "" "foo"}}"##, r##"foo"##),
211            (r##"{{ first_non_empty "" "foo" "bar"}}"##, r##"foo"##),
212            (r##"{{ first_non_empty "" null}}"##, r##""##),
213            (r##"{{ first_non_empty "" null 33}}"##, r##""##),
214            (r##"{{ first_non_empty "" null "foo" "bar"}}"##, r##"foo"##),
215        ]
216    }
217}