pub fn escape_python(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_rust(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn raw_string_hashes(s: &str) -> usize {
let mut max_hashes = 0;
let mut current = 0;
let mut after_quote = false;
for ch in s.chars() {
if ch == '"' {
after_quote = true;
current = 0;
} else if ch == '#' && after_quote {
current += 1;
max_hashes = max_hashes.max(current);
} else {
after_quote = false;
current = 0;
}
}
max_hashes + 1
}
pub fn rust_raw_string(s: &str) -> String {
let hashes = raw_string_hashes(s);
let h: String = "#".repeat(hashes);
format!("r{h}\"{s}\"{h}")
}
pub fn escape_js(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_js_template(s: &str) -> String {
s.replace('\\', "\\\\").replace('`', "\\`").replace('$', "\\$")
}
pub fn go_string_literal(s: &str) -> String {
if !s.contains('`') {
format!("`{s}`")
} else {
format!("\"{}\"", escape_go(s))
}
}
pub fn escape_go(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_java(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_csharp(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_php(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('$', "\\$")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_ruby(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('#', "\\#")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_ruby_single(s: &str) -> String {
s.replace('\\', "\\\\").replace('\'', "\\'")
}
pub fn ruby_needs_double_quotes(s: &str) -> bool {
s.contains('\n') || s.contains('\r') || s.contains('\t') || s.contains('\0')
}
pub fn ruby_string_literal(s: &str) -> String {
if ruby_needs_double_quotes(s) {
format!("\"{}\"", escape_ruby(s))
} else {
format!("'{}'", escape_ruby_single(s))
}
}
pub fn escape_elixir(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('#', "\\#")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_r(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_c(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn sanitize_ident(s: &str) -> String {
let mut result = String::with_capacity(s.len());
for ch in s.chars() {
if ch.is_ascii_alphanumeric() || ch == '_' {
result.push(ch);
} else {
result.push('_');
}
}
let trimmed = result.trim_start_matches(|c: char| c.is_ascii_digit());
if trimmed.is_empty() {
"_".to_string()
} else {
trimmed.to_string()
}
}
pub fn sanitize_filename(s: &str) -> String {
s.chars()
.map(|c| if c.is_ascii_alphanumeric() || c == '_' { c } else { '_' })
.collect::<String>()
.to_lowercase()
}
pub fn expand_fixture_templates(s: &str) -> String {
const PREFIX: &str = "{{ repeat '";
const SUFFIX: &str = " times }}";
let mut result = String::with_capacity(s.len());
let mut remaining = s;
while let Some(start) = remaining.find(PREFIX) {
result.push_str(&remaining[..start]);
let after_prefix = &remaining[start + PREFIX.len()..];
if let Some(quote_pos) = after_prefix.find("' ") {
let ch = &after_prefix[..quote_pos];
let after_quote = &after_prefix[quote_pos + 2..];
if let Some(end) = after_quote.find(SUFFIX) {
let count_str = after_quote[..end].trim();
if let Ok(count) = count_str.parse::<usize>() {
result.push_str(&ch.repeat(count));
remaining = &after_quote[end + SUFFIX.len()..];
continue;
}
}
}
result.push_str(PREFIX);
remaining = after_prefix;
}
result.push_str(remaining);
result
}
pub fn escape_shell(s: &str) -> String {
s.replace('\'', r"'\''")
}
pub fn escape_gleam(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
pub fn escape_zig(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}