pub fn yaml_string_needs_quotes(s: &str) -> bool {
if s.is_empty() {
return true;
}
if s.starts_with(char::is_whitespace) || s.ends_with(char::is_whitespace) {
return true;
}
if s.chars().any(|c| c.is_control()) {
return true;
}
if s.starts_with('-') {
return true;
}
if s.contains(":\n") || s.contains(": ") || s.ends_with(':') {
return true;
}
if s.contains(" #") {
return true;
}
if s.contains('\n') || s.contains('\r') {
return true;
}
if s.starts_with(['&', '*', ',', '?', '!', '>', '|', '@', '"', '\'', '#', '%']) {
return true;
}
if s.contains('"') || s.contains('\\') || s.contains('\'') {
return true;
}
if s.chars().any(|c| matches!(c, '{' | '}' | '`')) {
return true;
}
if s.starts_with('[') {
return true;
}
let lower = s.to_lowercase();
if matches!(
lower.as_str(),
"y" | "n" | "yes" | "no" | "true" | "false" | "on" | "off" | "null"
) {
return true;
}
if s.parse::<f64>().is_ok() {
return true;
}
false
}
pub fn yaml_escape_key_if_needed(s: &str) -> String {
if !yaml_string_needs_quotes(s) {
return s.to_string();
}
format!("'{}'", s.replace('\'', "''"))
}
pub fn yaml_escape_value_if_needed(s: &str) -> String {
if !yaml_string_needs_quotes(s) {
return s.to_string();
}
let mut result = String::from('"');
for ch in s.chars() {
match ch {
'\\' => result.push_str("\\\\"),
'"' => result.push_str("\\\""),
'\x08' => result.push_str("\\b"),
'\x0C' => result.push_str("\\f"),
'\n' => result.push_str("\\n"),
'\r' => result.push_str("\\r"),
'\t' => result.push_str("\\t"),
c if c.is_control() => {
result.push_str(&format!("\\x{:02x}", c as u32));
}
c => result.push(c),
}
}
result.push('"');
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_yaml_string_needs_quotes() {
assert!(yaml_string_needs_quotes("")); assert!(yaml_string_needs_quotes(" text")); assert!(yaml_string_needs_quotes("text ")); assert!(yaml_string_needs_quotes("-text")); assert!(yaml_string_needs_quotes("foo: bar")); assert!(yaml_string_needs_quotes("foo #bar")); assert!(yaml_string_needs_quotes("true")); assert!(yaml_string_needs_quotes("123")); assert!(yaml_string_needs_quotes("[array]"));
assert!(!yaml_string_needs_quotes("simple"));
assert!(!yaml_string_needs_quotes("hello-world"));
assert!(!yaml_string_needs_quotes("foo_bar"));
}
#[test]
fn test_yaml_escape_key_if_needed() {
assert_eq!(yaml_escape_key_if_needed("simple"), "simple");
assert_eq!(yaml_escape_key_if_needed("it's"), "'it''s'");
assert_eq!(yaml_escape_key_if_needed("-start"), "'-start'");
}
#[test]
fn test_yaml_escape_value_if_needed() {
assert_eq!(yaml_escape_value_if_needed("simple"), "simple");
assert_eq!(
yaml_escape_value_if_needed("hello\nworld"),
"\"hello\\nworld\""
);
assert_eq!(
yaml_escape_value_if_needed("quote\"here"),
"\"quote\\\"here\""
);
assert_eq!(
yaml_escape_value_if_needed("back\\slash"),
"\"back\\\\slash\""
);
}
}