@@ -19,6 +19,7 @@ pub(crate) fn add_filters(env: &mut Environment<'_>, target_config: &WeaverConfi
1919 env. add_filter ( "flatten" , flatten) ;
2020 env. add_filter ( "split_id" , split_id) ;
2121 env. add_filter ( "regex_replace" , regex_replace) ;
22+ env. add_filter ( "toyaml" , to_yaml) ;
2223}
2324
2425/// Add utility functions to the environment.
@@ -131,10 +132,34 @@ pub fn acronym(acronyms: Vec<String>) -> impl Fn(&str) -> String {
131132 }
132133}
133134
135+ // Helper filter to dump value (1st parameter) in yaml format.
136+ fn to_yaml ( value : & Value ) -> Result < Value , minijinja:: Error > {
137+ let yaml = serde_yaml:: to_string ( & value)
138+ . map_err ( |e| minijinja:: Error :: new ( ErrorKind :: BadSerialization , e. to_string ( ) ) )
139+ . map ( |s| {
140+ // When this filter is used the return value is safe for both HTML and JSON
141+ let mut rv = String :: with_capacity ( s. len ( ) ) ;
142+ for c in s. chars ( ) {
143+ match c {
144+ '<' => rv. push_str ( "\\ u003c" ) ,
145+ '>' => rv. push_str ( "\\ u003e" ) ,
146+ '&' => rv. push_str ( "\\ u0026" ) ,
147+ '\'' => rv. push_str ( "\\ u0027" ) ,
148+ _ => rv. push ( c) ,
149+ }
150+ }
151+ Value :: from_safe_string ( rv)
152+ } ) ?;
153+ Ok ( yaml)
154+ }
155+
134156#[ cfg( test) ]
135157mod tests {
158+ use std:: fs;
159+
136160 use crate :: extensions:: util:: add_filters;
137161 use minijinja:: Environment ;
162+ use serde_yaml:: { Mapping , Number , Value } ;
138163
139164 #[ test]
140165 fn test_regex_replace ( ) {
@@ -159,4 +184,58 @@ mod tests {
159184 "This A test with multiple A's"
160185 ) ;
161186 }
187+ #[ test]
188+ fn test_to_yaml ( ) {
189+ let mut env = Environment :: new ( ) ;
190+ let mut inner_map = Mapping :: new ( ) ;
191+ let _ = inner_map. insert (
192+ Value :: String ( "age" . to_owned ( ) ) ,
193+ Value :: Number ( Number :: from ( 30u64 ) ) ,
194+ ) ;
195+ let mut details_map = Mapping :: new ( ) ;
196+ let _ = details_map. insert (
197+ Value :: String ( "city" . to_owned ( ) ) ,
198+ Value :: String ( "Wonderland" . to_owned ( ) ) ,
199+ ) ;
200+ let _ = details_map. insert (
201+ Value :: String ( "email" . to_owned ( ) ) ,
202+ Value :: String ( "[email protected] " . to_owned ( ) ) , 203+ ) ;
204+ let _ = inner_map. insert (
205+ Value :: String ( "details" . to_owned ( ) ) ,
206+ Value :: Mapping ( details_map) ,
207+ ) ;
208+ let _ = inner_map. insert ( Value :: String ( "is_active" . to_owned ( ) ) , Value :: Bool ( true ) ) ;
209+ let _ = inner_map. insert (
210+ Value :: String ( "name" . to_owned ( ) ) ,
211+ Value :: String ( "Alice" . to_owned ( ) ) ,
212+ ) ;
213+ let _ = inner_map. insert (
214+ Value :: String ( "about" . to_owned ( ) ) ,
215+ Value :: String ( "<h1>Software Engineer<h1>" . to_owned ( ) ) ,
216+ ) ;
217+ let _ = inner_map. insert (
218+ Value :: String ( "profile" . to_owned ( ) ) ,
219+ Value :: String ( "https://example.com/?page=1§ion=about" . to_owned ( ) ) ,
220+ ) ;
221+ let _ = inner_map. insert (
222+ Value :: String ( "skills" . to_owned ( ) ) ,
223+ Value :: Sequence ( vec ! [
224+ Value :: String ( "Rust" . to_owned( ) ) ,
225+ Value :: String ( "JavaScript" . to_owned( ) ) ,
226+ ] ) ,
227+ ) ;
228+ let mut map = Mapping :: new ( ) ;
229+ let _ = map. insert ( Value :: String ( "user" . to_owned ( ) ) , Value :: Mapping ( inner_map) ) ;
230+
231+ let ctx = Value :: Mapping ( map) ;
232+ let config = crate :: config:: WeaverConfig :: default ( ) ;
233+
234+ add_filters ( & mut env, & config) ;
235+ let expected_yaml = fs:: read_to_string ( "expected_output/yaml/test.yaml" ) . unwrap ( ) ;
236+ assert_eq ! (
237+ env. render_str( "{{ user | toyaml }}" , & ctx) . unwrap( ) ,
238+ expected_yaml
239+ ) ;
240+ }
162241}
0 commit comments