difftreelog
refactor implement yaml escaping rules as in std.jsonnet
in: master
1 file changed
crates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth28 /// safe_key: 128 /// safe_key: 129 /// ```29 /// ```30 quote_keys: bool,30 quote_keys: bool,31 quote_values: bool,31 /// If true - then order of fields is preserved as written,32 /// If true - then order of fields is preserved as written,32 /// instead of sorting alphabetically33 /// instead of sorting alphabetically33 #[cfg(feature = "exp-preserve-order")]34 #[cfg(feature = "exp-preserve-order")]43 padding: Cow::Owned(padding.clone()),44 padding: Cow::Owned(padding.clone()),44 arr_element_padding: Cow::Owned(padding),45 arr_element_padding: Cow::Owned(padding),45 quote_keys: false,46 quote_keys: false,47 quote_values: false,46 #[cfg(feature = "exp-preserve-order")]48 #[cfg(feature = "exp-preserve-order")]47 preserve_order,49 preserve_order,48 }50 }56 padding: Cow::Borrowed(" "),58 padding: Cow::Borrowed(" "),57 arr_element_padding: Cow::Borrowed(if indent_array_in_object { " " } else { "" }),59 arr_element_padding: Cow::Borrowed(if indent_array_in_object { " " } else { "" }),58 quote_keys,60 quote_keys,61 quote_values: true,59 #[cfg(feature = "exp-preserve-order")]62 #[cfg(feature = "exp-preserve-order")]60 preserve_order,63 preserve_order,61 }64 }67 }70 }68}71}697270/// From <https://github.com/chyh1990/yaml-rust/blob/da52a68615f2ecdd6b7e4567019f280c433c1521/src/emitter.rs#L289>71/// With added date check72fn yaml_needs_quotes(string: &str) -> bool {73fn bare_safe(key: &str) -> bool {74 fn count_char_u(k: &str, c: char) -> usize {75 let cu = c.to_ascii_uppercase();76 k.chars().filter(|v| *v == c || *v == cu).count()77 }78 fn count_char(k: &str, c: char) -> usize {79 k.chars().filter(|v| *v == c).count()80 }73 fn need_quotes_spaces(string: &str) -> bool {81 fn is_reserved(key: &str) -> bool {82 const RESERVED: &[&str] = &[83 // Boolean types taken from https://yaml.org/type/bool.html84 "true", "false", "yes", "no", "on", "off", "y", "n",85 // Numerical words taken from https://yaml.org/type/float.html86 ".nan", "-.inf", "+.inf", ".inf", "null",87 // Invalid keys that contain no invalid characters88 "-", "---", "",89 ];74 string.starts_with(' ') || string.ends_with(' ')90 RESERVED.iter().any(|k| key.eq_ignore_ascii_case(k))75 }91 }769277 string.is_empty()93 // Check for unsafe characters78 || need_quotes_spaces(string)94 if !key79 || string.starts_with(['&' , '*' , '?' , '|' , '-' , '<' , '>' , '=' , '!' , '%' , '@'])95 .chars()80 || string.contains(|c| matches!(c, ':' | '{' | '}' | '[' | ']' | ',' | '#' | '`' | '\"' | '\'' | '\\' | '\0'..='\x06' | '\t' | '\n' | '\r' | '\x0e'..='\x1a' | '\x1c'..='\x1f'))96 .all(|v| matches!(v, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '/'))81 || [97 {82 // http://yaml.org/type/bool.html98 return false;83 "yes", "Yes", "YES", "no", "No", "NO", "True", "TRUE", "true", "False", "FALSE", "false",99 }84 "on", "On", "ON", "off", "Off", "OFF", // http://yaml.org/type/null.html100 // Check for reserved words85 "null", "Null", "NULL", "~",101 if is_reserved(key) {86 // > Quoted in std.jsonnet, however, in serde_yaml they were quoted:102 return false;87 // > Note: 'y', 'Y', 'n', 'N', is not quoted deliberately, as in libyaml. PyYAML also parse103 }88 // > them as string, not booleans, although it is violating the YAML 1.1 specification.104 // Check for timestamp values. Since spaces and colons are already forbidden,89 // > See https://github.com/dtolnay/serde-yaml/pull/83#discussion_r152628088.105 // all that could potentially pass is the standard date format (ex MM-DD-YYYY, YYYY-DD-MM, etc).90 "y", "Y", "n", "N",106 // This check is even more conservative: Keys that meet all of the following:91 "-.inf", "+.inf", ".inf",107 // - all characters match [0-9\-]92 "-", "---", ""108 // - has exactly 2 dashes109 // are considered dates.93 ].contains(&string)110 if key.chars().all(|v| matches!(v, '0'..='9' | '-')) && count_char(key, '-') == 2 {111 return false;112 }113 // Check for integers. Keys that meet all of the following:114 // - all characters match [0-9_\-]115 // - has at most 1 dash116 // are considered integers.94 || (string.chars().all(|c| matches!(c, '0'..='9' | '-'))117 else if key.chars().all(|v| matches!(v, '0'..='9' | '-' | '_')) && count_char(key, '-') < 2 {95 && string.chars().filter(|c| *c == '-').count() == 2)118 return false;96 || string.starts_with('.')119 }120 // Check for binary integers. Keys that meet all of the following:121 // - all characters match [0-9b_\-]122 // - has at least 3 characters123 // - starts with (-)0b124 // are considered binary integers.125 else if key126 .chars()127 .all(|v| matches!(v, '0'..='9' | '-' | '_' | 'b' | 'B'))97 || string.starts_with("0x")128 && (key.starts_with("0b") || key.starts_with("-0b"))98 || string.parse::<i64>().is_ok()129 && key.len() > 2130 {131 return false;132 }133 // Check for floats. Keys that meet all of the following:134 // - all characters match [0-9e._\-]135 // - has at most a single period136 // - has at most two dashes137 // - has at most 1 'e'138 // are considered floats.139 else if key140 .chars()141 .all(|v| matches!(v, '0'..='9' | '-' | '_' | 'e' | 'E' | '.'))142 && count_char_u(key, 'e') < 2143 && count_char(key, '-') < 3144 && count_char(key, '.') <= 1145 {146 return false;147 }148 // Check for hexadecimals. Keys that meet all of the following:149 // - all characters match [0-9a-fx_\-]150 // - has at most 1 dash151 // - has at least 3 characters152 // - starts with (-)0x153 // are considered hexadecimals.154 else if key155 .chars()99 || string.parse::<f64>().is_ok()156 .all(|v| matches!(v, '0'..='9' | '-' | '_' | 'x' | 'X' | 'a'..='f' | 'A'..='F' ))157 && key.len() >= 3158 && count_char(key, '-') < 2159 && (key.starts_with("-0x") || key.starts_with("0x"))160 {161 return false;162 }163 true100}164}101165102#[allow(dead_code)]166#[allow(dead_code)]142 buf.push_str(&options.padding);206 buf.push_str(&options.padding);143 buf.push_str(line);207 buf.push_str(line);144 }208 }145 } else if !options.quote_keys && !yaml_needs_quotes(&s) {209 } else if !options.quote_values && bare_safe(&s) {146 buf.push_str(&s);210 buf.push_str(&s);147 } else {211 } else {148 escape_string_json_buf(&s, buf);212 escape_string_json_buf(&s, buf);203 buf.push('\n');267 buf.push('\n');204 buf.push_str(cur_padding);268 buf.push_str(cur_padding);205 }269 }206 if !options.quote_keys && !yaml_needs_quotes(&key) {270 if !options.quote_keys && bare_safe(&key) {207 buf.push_str(&key);271 buf.push_str(&key);208 } else {272 } else {209 escape_string_json_buf(&key, buf);273 escape_string_json_buf(&key, buf);