use std::borrow::Cow;
const SINGLE: char = '\'';
const BRACKETL: char = '⟨';
const BRACKETR: char = '⟩';
const BRACKET_ESC: &str = r"\⟩";
const DOUBLE: char = '"';
const DOUBLE_ESC: &str = r#"\""#;
const BACKTICK: char = '`';
const BACKTICK_ESC: &str = r"\`";
#[inline]
pub fn quote_str(s: &str) -> String {
let mut ret = String::with_capacity(2 + s.len());
fn escape_into(into: &mut String, s: &str, escape_double: bool) {
let mut last_end = 0;
for (start, part) in s.match_indices(|c| c == '\\' || (c == DOUBLE && escape_double)) {
into.push_str(&s[last_end..start]);
into.push_str(if part == "\\" {
"\\\\"
} else {
DOUBLE_ESC
});
last_end = start + part.len();
}
into.push_str(&s[last_end..s.len()]);
}
let quote = if s.contains(SINGLE) {
DOUBLE
} else {
SINGLE
};
ret.push(quote);
escape_into(&mut ret, s, quote == DOUBLE);
ret.push(quote);
ret
}
#[inline]
pub fn quote_plain_str(s: &str) -> String {
quote_str(s)
}
#[inline]
pub fn escape_key(s: &str) -> Cow<'_, str> {
escape_normal(s, DOUBLE, DOUBLE, DOUBLE_ESC)
}
#[inline]
pub fn escape_rid(s: &str) -> Cow<'_, str> {
escape_full_numeric(s, BRACKETL, BRACKETR, BRACKET_ESC)
}
#[inline]
pub fn escape_ident(s: &str) -> Cow<'_, str> {
if let Some(x) = escape_reserved_keyword(s) {
return Cow::Owned(x);
}
escape_starts_numeric(s, BACKTICK, BACKTICK, BACKTICK_ESC)
}
#[inline]
pub fn escape_normal<'a>(s: &'a str, l: char, r: char, e: &str) -> Cow<'a, str> {
if let Some(x) = s.bytes().next() {
if x.is_ascii_digit() {
return Cow::Owned(format!("{l}{}{r}", s.replace(r, e)));
}
}
if s.bytes().all(|x| x.is_ascii_alphanumeric() || x == b'_') {
return Cow::Borrowed(s);
}
Cow::Owned(format!("{l}{}{r}", s.replace(r, e)))
}
pub fn escape_reserved_keyword(s: &str) -> Option<String> {
crate::syn::could_be_reserved_keyword(s).then(|| format!("`{}`", s))
}
#[inline]
pub fn escape_full_numeric<'a>(s: &'a str, l: char, r: char, e: &str) -> Cow<'a, str> {
let mut numeric = true;
for x in s.bytes() {
if !(x.is_ascii_alphanumeric() || x == b'_') {
return Cow::Owned(format!("{l}{}{r}", s.replace(r, e)));
}
if numeric && !x.is_ascii_digit() {
numeric = false;
}
}
if numeric {
return Cow::Owned(format!("{l}{}{r}", s.replace(r, e)));
}
Cow::Borrowed(s)
}
#[inline]
pub fn escape_starts_numeric<'a>(s: &'a str, l: char, r: char, e: &str) -> Cow<'a, str> {
for (idx, x) in s.bytes().enumerate() {
if idx == 0 && x.is_ascii_digit() {
return Cow::Owned(format!("{l}{}{r}", s.replace(r, e)));
}
if !(x.is_ascii_alphanumeric() || x == b'_') {
return Cow::Owned(format!("{l}{}{r}", s.replace(r, e)));
}
}
Cow::Borrowed(s)
}