@@ -173,6 +173,62 @@ pub struct ManifestYamlOptions<'s> {
173173 /// ## <- this
174174 /// ```
175175 pub arr_element_padding : & ' s str ,
176+ /// Should yaml keys appear unescaped, when possible
177+ /// ```yaml
178+ /// "safe_key": 1
179+ /// # vs
180+ /// safe_key: 1
181+ /// ```
182+ pub quote_keys : bool ,
183+ }
184+
185+ /// From https://github.com/chyh1990/yaml-rust/blob/da52a68615f2ecdd6b7e4567019f280c433c1521/src/emitter.rs#L289
186+ /// With added date check
187+ fn yaml_needs_quotes ( string : & str ) -> bool {
188+ fn need_quotes_spaces ( string : & str ) -> bool {
189+ string. starts_with ( ' ' ) || string. ends_with ( ' ' )
190+ }
191+
192+ string == ""
193+ || need_quotes_spaces ( string)
194+ || string. starts_with ( |character : char | match character {
195+ '&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@' => true ,
196+ _ => false ,
197+ } ) || string. contains ( |character : char | match character {
198+ ':'
199+ | '{'
200+ | '}'
201+ | '['
202+ | ']'
203+ | ','
204+ | '#'
205+ | '`'
206+ | '\"'
207+ | '\''
208+ | '\\'
209+ | '\0' ..='\x06'
210+ | '\t'
211+ | '\n'
212+ | '\r'
213+ | '\x0e' ..='\x1a'
214+ | '\x1c' ..='\x1f' => true ,
215+ _ => false ,
216+ } ) || [
217+ // http://yaml.org/type/bool.html
218+ // Note: 'y', 'Y', 'n', 'N', is not quoted deliberately, as in libyaml. PyYAML also parse
219+ // them as string, not booleans, although it is violating the YAML 1.1 specification.
220+ // See https://github.com/dtolnay/serde-yaml/pull/83#discussion_r152628088.
221+ "yes" , "Yes" , "YES" , "no" , "No" , "NO" , "True" , "TRUE" , "true" , "False" , "FALSE" , "false" ,
222+ "on" , "On" , "ON" , "off" , "Off" , "OFF" , // http://yaml.org/type/null.html
223+ "null" , "Null" , "NULL" , "~" ,
224+ ]
225+ . contains ( & string)
226+ || ( string. chars ( ) . all ( |c| matches ! ( c, '0' ..='9' | '-' ) )
227+ && string. chars ( ) . filter ( |c| * c == '-' ) . count ( ) == 2 )
228+ || string. starts_with ( '.' )
229+ || string. starts_with ( "0x" )
230+ || string. parse :: < i64 > ( ) . is_ok ( )
231+ || string. parse :: < f64 > ( ) . is_ok ( )
176232}
177233
178234pub fn manifest_yaml_ex ( val : & Val , options : & ManifestYamlOptions < ' _ > ) -> Result < String > {
@@ -206,8 +262,10 @@ fn manifest_yaml_ex_buf(
206262 buf. push_str ( options. padding ) ;
207263 buf. push_str ( line) ;
208264 }
265+ } else if !options. quote_keys && !yaml_needs_quotes ( & s) {
266+ buf. push_str ( & s) ;
209267 } else {
210- escape_string_json_buf ( s, buf)
268+ escape_string_json_buf ( s, buf) ;
211269 }
212270 }
213271 Val :: Num ( n) => write ! ( buf, "{}" , * n) . unwrap ( ) ,
@@ -253,7 +311,11 @@ fn manifest_yaml_ex_buf(
253311 buf. push ( '\n' ) ;
254312 buf. push_str ( cur_padding) ;
255313 }
256- escape_string_json_buf ( key, buf) ;
314+ if !options. quote_keys && !yaml_needs_quotes ( & key) {
315+ buf. push_str ( & key) ;
316+ } else {
317+ escape_string_json_buf ( key, buf) ;
318+ }
257319 buf. push ( ':' ) ;
258320 let prev_len = cur_padding. len ( ) ;
259321 let item = o. get ( key. clone ( ) ) ?. expect ( "field exists" ) ;
0 commit comments