pub fn escape_single_quotes(value: &str) -> String {
value.replace('\'', "''")
}
pub fn in_list_predicate(column: &str, values: &[String]) -> String {
debug_assert!(!values.is_empty());
let values = values
.iter()
.map(|value| format!("'{}'", escape_single_quotes(value)))
.collect::<Vec<_>>()
.join(",");
format!("{} IN ({})", column, values)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_quotes_unchanged() {
assert_eq!(escape_single_quotes("src/main.rs"), "src/main.rs");
}
#[test]
fn single_quote_is_doubled() {
assert_eq!(escape_single_quotes("src/it's.rs"), "src/it''s.rs");
}
#[test]
fn multiple_quotes_all_doubled() {
assert_eq!(escape_single_quotes("a'b'c"), "a''b''c");
assert_eq!(escape_single_quotes("''"), "''''");
}
#[test]
fn empty_string() {
assert_eq!(escape_single_quotes(""), "");
}
#[test]
fn produces_balanced_predicate() {
let p = "weird/o'brien.rs";
let predicate = format!("path = '{}'", escape_single_quotes(p));
assert_eq!(predicate, "path = 'weird/o''brien.rs'");
}
#[test]
fn in_list_with_single_value() {
assert_eq!(
in_list_predicate("path", &["src/main.rs".to_string()]),
"path IN ('src/main.rs')"
);
}
#[test]
fn in_list_with_multiple_values() {
assert_eq!(
in_list_predicate("path", &["src/a.rs".to_string(), "src/b.rs".to_string()]),
"path IN ('src/a.rs','src/b.rs')"
);
}
#[test]
fn in_list_escapes_apostrophes() {
assert_eq!(
in_list_predicate("path", &["src/it's.rs".to_string()]),
"path IN ('src/it''s.rs')"
);
}
}