11use std:: collections:: HashMap ;
22
33use crate :: * ;
4- use handlebars:: { Handlebars , html_escape} ;
4+ use handlebars:: {
5+ Context , Handlebars , Helper , HelperResult , Output , RenderContext , RenderErrorReason ,
6+ html_escape,
7+ } ;
8+
9+ /// Custom helper that renders a value without HTML escaping.
10+ /// Usage: {{raw var}}
11+ fn raw_helper (
12+ h : & Helper ,
13+ _: & Handlebars ,
14+ _: & Context ,
15+ _: & mut RenderContext ,
16+ out : & mut dyn Output ,
17+ ) -> HelperResult {
18+ let param = h
19+ . param ( 0 )
20+ . ok_or ( RenderErrorReason :: Other ( "Parameter not found" . into ( ) ) ) ?;
21+ let value = param. value ( ) ;
22+
23+ // Output the raw JSON value without HTML escaping
24+ if let Some ( s) = value. as_str ( ) {
25+ out. write ( s) ?;
26+ } else {
27+ out. write ( & value. to_string ( ) ) ?;
28+ }
29+ Ok ( ( ) )
30+ }
531
632/// Applies a parsed template to the specified data object and
733/// returns the string output.
@@ -19,6 +45,10 @@ pub unsafe extern "C-unwind" fn kcl_template_execute(
1945
2046 if let Some ( template) = get_call_arg_str ( args, kwargs, 0 , Some ( "template" ) ) {
2147 let mut handlebars = Handlebars :: new ( ) ;
48+
49+ // Register helper for raw (unescaped) output
50+ handlebars. register_helper ( "raw" , Box :: new ( raw_helper) ) ;
51+
2252 handlebars
2353 . register_template_string ( "template" , template)
2454 . expect ( "register template failed" ) ;
@@ -53,5 +83,140 @@ pub unsafe extern "C-unwind" fn kcl_template_html_escape(
5383 if let Some ( data) = get_call_arg_str ( args, kwargs, 0 , Some ( "data" ) ) {
5484 return ValueRef :: str ( & html_escape ( & data) ) . into_raw ( ctx) ;
5585 }
56- panic ! ( "html_escape() takes exactly one argument (0 given)" ) ;
86+ panic ! ( "html_escape() takes exactly one argument (0 given)" )
87+ }
88+
89+ #[ cfg( test) ]
90+ mod tests {
91+ use super :: * ;
92+
93+ #[ test]
94+ fn test_raw_helper_with_html ( ) {
95+ let mut handlebars = Handlebars :: new ( ) ;
96+ handlebars. register_helper ( "raw" , Box :: new ( raw_helper) ) ;
97+
98+ let template = r#"{{raw value}}"# . to_string ( ) ;
99+ handlebars
100+ . register_template_string ( "test" , template)
101+ . expect ( "register template failed" ) ;
102+
103+ let mut data = serde_json:: map:: Map :: new ( ) ;
104+ data. insert (
105+ "value" . to_string ( ) ,
106+ serde_json:: Value :: String ( "<div>test</div>" . to_string ( ) ) ,
107+ ) ;
108+
109+ let result = handlebars. render ( "test" , & data) . expect ( "render failed" ) ;
110+ assert_eq ! ( result, "<div>test</div>" ) ;
111+ }
112+
113+ #[ test]
114+ fn test_raw_helper_with_quotes ( ) {
115+ let mut handlebars = Handlebars :: new ( ) ;
116+ handlebars. register_helper ( "raw" , Box :: new ( raw_helper) ) ;
117+
118+ let template = r#"{{raw value}}"# . to_string ( ) ;
119+ handlebars
120+ . register_template_string ( "test" , template)
121+ . expect ( "register template failed" ) ;
122+
123+ let mut data = serde_json:: map:: Map :: new ( ) ;
124+ data. insert (
125+ "value" . to_string ( ) ,
126+ serde_json:: Value :: String ( r#"timeout: "5m""# . to_string ( ) ) ,
127+ ) ;
128+
129+ let result = handlebars. render ( "test" , & data) . expect ( "render failed" ) ;
130+ assert_eq ! ( result, r#"timeout: "5m""# ) ;
131+ }
132+
133+ #[ test]
134+ fn test_raw_helper_with_yaml_like_content ( ) {
135+ let mut handlebars = Handlebars :: new ( ) ;
136+ handlebars. register_helper ( "raw" , Box :: new ( raw_helper) ) ;
137+
138+ let template = r#"{{raw value}}"# . to_string ( ) ;
139+ handlebars
140+ . register_template_string ( "test" , template)
141+ . expect ( "register template failed" ) ;
142+
143+ let yaml_content = r#"config:
144+ timeout: "5m""# ;
145+ let mut data = serde_json:: map:: Map :: new ( ) ;
146+ data. insert (
147+ "value" . to_string ( ) ,
148+ serde_json:: Value :: String ( yaml_content. to_string ( ) ) ,
149+ ) ;
150+
151+ let result = handlebars. render ( "test" , & data) . expect ( "render failed" ) ;
152+ assert_eq ! ( result, yaml_content) ;
153+ }
154+
155+ #[ test]
156+ fn test_normal_template_still_escapes ( ) {
157+ let mut handlebars = Handlebars :: new ( ) ;
158+ handlebars. register_helper ( "raw" , Box :: new ( raw_helper) ) ;
159+
160+ // Test that normal {{var}} still escapes
161+ let template = r#"{{value}}"# . to_string ( ) ;
162+ handlebars
163+ . register_template_string ( "test" , template)
164+ . expect ( "register template failed" ) ;
165+
166+ let mut data = serde_json:: map:: Map :: new ( ) ;
167+ data. insert (
168+ "value" . to_string ( ) ,
169+ serde_json:: Value :: String ( "<div>test</div>" . to_string ( ) ) ,
170+ ) ;
171+
172+ let result = handlebars. render ( "test" , & data) . expect ( "render failed" ) ;
173+ // Normal syntax should escape
174+ assert_eq ! ( result, "<div>test</div>" ) ;
175+ }
176+
177+ #[ test]
178+ fn test_triple_brace_syntax ( ) {
179+ let mut handlebars = Handlebars :: new ( ) ;
180+ handlebars. register_helper ( "raw" , Box :: new ( raw_helper) ) ;
181+
182+ // Test standard Handlebars triple brace syntax
183+ let template = r#"{{{value}}}"# . to_string ( ) ;
184+ handlebars
185+ . register_template_string ( "test" , template)
186+ . expect ( "register template failed" ) ;
187+
188+ let mut data = serde_json:: map:: Map :: new ( ) ;
189+ data. insert (
190+ "value" . to_string ( ) ,
191+ serde_json:: Value :: String ( "<div>test</div>" . to_string ( ) ) ,
192+ ) ;
193+
194+ let result = handlebars. render ( "test" , & data) . expect ( "render failed" ) ;
195+ // Triple braces should not escape
196+ assert_eq ! ( result, "<div>test</div>" ) ;
197+ }
198+
199+ #[ test]
200+ fn test_raw_vs_normal_syntax ( ) {
201+ let mut handlebars = Handlebars :: new ( ) ;
202+ handlebars. register_helper ( "raw" , Box :: new ( raw_helper) ) ;
203+
204+ let template = r#"normal: {{value}}, raw: {{raw value}}, triple: {{{value}}}"# . to_string ( ) ;
205+ handlebars
206+ . register_template_string ( "test" , template)
207+ . expect ( "register template failed" ) ;
208+
209+ let mut data = serde_json:: map:: Map :: new ( ) ;
210+ data. insert (
211+ "value" . to_string ( ) ,
212+ serde_json:: Value :: String ( r#""test""# . to_string ( ) ) ,
213+ ) ;
214+
215+ let result = handlebars. render ( "test" , & data) . expect ( "render failed" ) ;
216+ // normal should escape quotes to ", raw and triple should not
217+ assert_eq ! (
218+ result,
219+ r#"normal: "test", raw: "test", triple: "test""#
220+ ) ;
221+ }
57222}
0 commit comments