use crate::SyntaxNode;
use crate::generated::keywords::RESERVED_KEYWORDS;
pub fn quote_column_alias(text: &str) -> String {
if needs_quoting(text) {
format!(r#""{}""#, text.replace('"', r#""""#))
} else {
text.to_string()
}
}
pub fn unquote_ident(node: &SyntaxNode) -> Option<String> {
let text = node.text().to_string();
if !text.starts_with('"') || !text.ends_with('"') {
return None;
}
let text = &text[1..text.len() - 1];
if is_reserved_word(text) {
return None;
}
if text.is_empty() {
return None;
}
let mut chars = text.chars();
match chars.next() {
Some(c) if c.is_lowercase() || c == '_' => {}
_ => return None,
}
for c in chars {
if c.is_lowercase() || c.is_ascii_digit() || c == '_' || c == '$' {
continue;
}
return None;
}
Some(text.to_string())
}
pub fn needs_quoting(text: &str) -> bool {
if text.is_empty() {
return true;
}
let mut chars = text.chars();
match chars.next() {
Some(c) if c.is_lowercase() || c == '_' => {}
_ => return true,
}
for c in chars {
if c.is_lowercase() || c.is_ascii_digit() || c == '_' || c == '$' {
continue;
}
return true;
}
false
}
pub fn is_reserved_word(text: &str) -> bool {
RESERVED_KEYWORDS
.binary_search(&text.to_ascii_lowercase().as_str())
.is_ok()
}
pub fn strip_quotes(text: &str) -> Option<&str> {
text.strip_prefix('\'')?.strip_suffix('\'')
}
pub fn strip_prefixed_quotes(text: &str, prefix: [char; 2]) -> Option<&str> {
strip_quotes(text.strip_prefix(prefix)?)
}
pub fn strip_unicode_esc_prefix(text: &str) -> Option<&str> {
strip_quotes(text.strip_prefix(['u', 'U'])?.strip_prefix('&')?)
}
pub fn strip_dollar_quotes(text: &str) -> Option<&str> {
let after_first = text.strip_prefix('$')?;
let tag_end = after_first.find('$')?;
let tag = &after_first[..tag_end];
let body = &after_first[tag_end + 1..];
let closing = format!("${tag}$");
body.strip_suffix(&closing)
}
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use super::*;
#[test]
fn quote_column_alias_handles_embedded_quotes() {
assert_snapshot!(quote_column_alias(r#"foo"bar"#), @r#""foo""bar""#);
}
#[test]
fn quote_column_alias_doesnt_quote_reserved_words() {
assert_snapshot!(quote_column_alias("case"), @"case");
assert_snapshot!(quote_column_alias("array"), @"array");
}
#[test]
fn quote_column_alias_doesnt_quote_simple_identifiers() {
assert_snapshot!(quote_column_alias("col_name"), @"col_name");
}
#[test]
fn quote_column_alias_handles_special_column_name() {
assert_snapshot!(quote_column_alias("?column?"), @r#""?column?""#);
}
}