use std::fmt;
use std::fmt::{Display, Formatter};
use crate::write_utils::Indent;
use std::str::FromStr;
use crate::antlr::hamelinparser::{
IdentifierContextAll, IdentifierContextAttrs, SimpleIdentifierContextAll,
};
use crate::antlr::{parse_identifier, parse_simple_identifier};
use crate::err::{TranslationError, TranslationErrors};
use antlr_rust::tree::ParseTree;
use derive_more::{From, TryUnwrap};
use std::rc::Rc;
#[derive(Clone, Debug, PartialEq, Hash, Eq, From, TryUnwrap)]
pub enum Identifier {
Simple(SimpleIdentifier),
Compound(CompoundIdentifier),
}
impl Identifier {
pub fn to_hamelin(&self) -> String {
match self {
Identifier::Simple(simple) => simple.to_hamelin(),
Identifier::Compound(compound) => compound.to_hamelin(),
}
}
pub fn simples(&self) -> Vec<SimpleIdentifier> {
match self {
Identifier::Simple(simple) => vec![simple.clone()],
Identifier::Compound(compound) => compound.as_slice().to_vec(),
}
}
pub fn prefixed_with(&self, prefix: &SimpleIdentifier) -> CompoundIdentifier {
let rest = self.simples();
CompoundIdentifier::new(prefix.clone(), rest[0].clone(), rest[1..].to_vec())
}
pub fn from_simples(simples: Vec<SimpleIdentifier>) -> Option<Identifier> {
match simples.as_slice() {
[] => None,
[single] => Some(single.clone().into()),
[first, second, rest @ ..] => {
Some(CompoundIdentifier::new(first.clone(), second.clone(), rest.to_vec()).into())
}
}
}
pub fn prefixed_with_str(&self, prefix: &str) -> CompoundIdentifier {
self.prefixed_with(&SimpleIdentifier::new(prefix))
}
pub fn prefix(&self) -> &[SimpleIdentifier] {
match self {
Identifier::Simple(_) => &[],
Identifier::Compound(compound) => {
let s = compound.as_slice();
&s[..s.len() - 1]
}
}
}
pub fn last(&self) -> &SimpleIdentifier {
match self {
Identifier::Simple(simple) => simple,
Identifier::Compound(compound) => {
let s = compound.as_slice();
&s[s.len() - 1]
}
}
}
pub fn first(&self) -> &SimpleIdentifier {
match self {
Identifier::Simple(simple) => simple,
Identifier::Compound(compound) => compound.first(),
}
}
pub fn rest(&self) -> &[SimpleIdentifier] {
match self {
Identifier::Simple(_) => &[],
Identifier::Compound(compound) => &compound.as_slice()[1..],
}
}
pub fn fmt_indented(
&self,
f: &mut std::fmt::Formatter<'_>,
indentation: Indent,
) -> std::fmt::Result {
match self {
Identifier::Simple(simple) => simple.fmt_indented(f, indentation),
Identifier::Compound(compound) => compound.fmt_indented(f, indentation),
}
}
pub fn prepend(self, id: Identifier) -> CompoundIdentifier {
CompoundIdentifier::from_two_idents(id, self.into())
}
}
impl Display for Identifier {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
impl FromStr for Identifier {
type Err = TranslationErrors;
fn from_str(s: &str) -> Result<Self, Self::Err> {
HamelinIdentifier::new(parse_identifier(s.to_string())?).to_sql()
}
}
impl From<crate::tree::ast::identifier::Identifier> for Identifier {
fn from(ast_ident: crate::tree::ast::identifier::Identifier) -> Self {
match ast_ident {
crate::tree::ast::identifier::Identifier::Simple(simple) => {
Identifier::Simple(simple.into())
}
crate::tree::ast::identifier::Identifier::Compound(compound) => {
Identifier::Compound(compound.into())
}
}
}
}
impl From<crate::tree::ast::identifier::SimpleIdentifier> for Identifier {
fn from(ast_ident: crate::tree::ast::identifier::SimpleIdentifier) -> Self {
Identifier::Simple(ast_ident.into())
}
}
impl From<crate::tree::ast::identifier::CompoundIdentifier> for Identifier {
fn from(ast_ident: crate::tree::ast::identifier::CompoundIdentifier) -> Self {
Identifier::Compound(ast_ident.into())
}
}
#[derive(Clone, Debug, PartialEq, Hash, Eq)]
pub struct SimpleIdentifier {
pub name: String,
}
impl SimpleIdentifier {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
pub fn from_parse_tree(tree: &SimpleIdentifierContextAll) -> Result<Self, TranslationError> {
match tree {
SimpleIdentifierContextAll::BackQuotedIdentifierContext(ctx) => {
let raw = ctx.get_text();
let text = raw
.get(1..raw.len().saturating_sub(1))
.ok_or_else(|| TranslationError::msg(ctx, "invalid back-quoted identifier"))?
.replace("``", "`");
Ok(Self::new(&text))
}
SimpleIdentifierContextAll::UnquotedIdentifierContext(ctx) => {
Ok(Self::new(&ctx.get_text()))
}
SimpleIdentifierContextAll::Error(ctx) => {
Err(TranslationError::msg(ctx, "parse_error"))
}
}
}
pub fn is_clean(&self) -> bool {
match parse_simple_identifier(self.name.clone()) {
Ok(tree) => match tree.as_ref() {
SimpleIdentifierContextAll::BackQuotedIdentifierContext(_) => true,
SimpleIdentifierContextAll::UnquotedIdentifierContext(_) => true,
SimpleIdentifierContextAll::Error(_) => false,
},
Err(_) => false,
}
}
pub fn to_hamelin(&self) -> String {
if self.is_clean() {
self.name.clone()
} else {
format!("`{}`", self.name.replace("`", "``"))
}
}
pub fn fmt_indented(&self, f: &mut Formatter<'_>, _indentation: Indent) -> std::fmt::Result {
write!(f, "\"{}\"", self.name.replace("\"", "\"\""))
}
}
impl From<crate::tree::ast::identifier::SimpleIdentifier> for SimpleIdentifier {
fn from(ast_ident: crate::tree::ast::identifier::SimpleIdentifier) -> Self {
SimpleIdentifier::new(ast_ident.as_str())
}
}
impl Display for SimpleIdentifier {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
impl From<SimpleIdentifier> for String {
fn from(ident: SimpleIdentifier) -> Self {
ident.name
}
}
impl FromStr for SimpleIdentifier {
type Err = TranslationErrors;
fn from_str(s: &str) -> Result<Self, Self::Err> {
HamelinSimpleIdentifier::new(parse_simple_identifier(s.to_string())?).to_sql()
}
}
#[derive(Clone, Debug, PartialEq, Hash, Eq)]
pub struct CompoundIdentifier {
simples: Vec<SimpleIdentifier>,
}
impl CompoundIdentifier {
pub fn new(
first: SimpleIdentifier,
second: SimpleIdentifier,
rest: Vec<SimpleIdentifier>,
) -> Self {
let mut simples = vec![first, second];
simples.extend(rest);
Self { simples }
}
pub fn as_slice(&self) -> &[SimpleIdentifier] {
&self.simples
}
pub fn into_vec(self) -> Vec<SimpleIdentifier> {
self.simples
}
pub fn to_hamelin(&self) -> String {
self.simples
.iter()
.map(|s| s.to_hamelin())
.collect::<Vec<String>>()
.join(".")
}
pub fn from_two_idents(first: Identifier, second: Identifier) -> Self {
Self::from_idents(first, second, &[])
}
pub fn from_idents(first: Identifier, second: Identifier, rest: &[Identifier]) -> Self {
let all: Vec<_> = [first]
.iter()
.chain([second].iter())
.chain(rest.iter())
.flat_map(|id| match id {
Identifier::Simple(simple) => vec![simple.clone()],
Identifier::Compound(compound) => compound.as_slice().to_vec(),
})
.collect();
match all.as_slice() {
[first, second, rest @ ..] => Self::new(first.clone(), second.clone(), rest.to_vec()),
_ => unreachable!("There are always at least two identifiers passed to from_idents"),
}
}
pub fn from_two_str(one: &str, two: &str) -> Self {
Self::new(
SimpleIdentifier::new(one),
SimpleIdentifier::new(two),
vec![],
)
}
pub fn first(&self) -> &SimpleIdentifier {
&self.simples[0]
}
pub fn rest(&self) -> Identifier {
if self.simples.len() == 2 {
self.simples[1].clone().into()
} else {
CompoundIdentifier::new(
self.simples[1].clone(),
self.simples[2].clone(),
self.simples[3..].to_vec(),
)
.into()
}
}
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> std::fmt::Result {
for (i, simple) in self.simples.iter().enumerate() {
simple.fmt_indented(f, indentation)?;
if i < self.simples.len() - 1 {
write!(f, ".")?;
}
}
Ok(())
}
}
impl Display for CompoundIdentifier {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
impl From<crate::tree::ast::identifier::CompoundIdentifier> for CompoundIdentifier {
fn from(ast_ident: crate::tree::ast::identifier::CompoundIdentifier) -> Self {
let all: Vec<SimpleIdentifier> = ast_ident
.into_parts()
.into_iter()
.map(|p| p.into())
.collect();
CompoundIdentifier::new(all[0].clone(), all[1].clone(), all[2..].to_vec())
}
}
pub struct HamelinIdentifier {
pub ctx: Rc<IdentifierContextAll<'static>>,
}
impl HamelinIdentifier {
pub fn new(ctx: Rc<IdentifierContextAll<'static>>) -> Self {
Self { ctx }
}
pub fn to_sql(&self) -> Result<Identifier, TranslationErrors> {
let idents = TranslationErrors::from_vec(
self.ctx
.simpleIdentifier_all()
.iter()
.map(|si| HamelinSimpleIdentifier::new(si.clone()).to_sql())
.collect(),
)?;
if idents.len() == 1 {
Ok(idents[0].clone().into())
} else if idents.is_empty() {
Err(TranslationError::msg(self.ctx.as_ref(), "expected identifier").single())
} else {
match idents.as_slice() {
[first, second, rest @ ..] => {
Ok(
CompoundIdentifier::new(first.clone(), second.clone(), rest.to_vec())
.into(),
)
}
_ => Err(TranslationError::msg(
self.ctx.as_ref(),
"expected at least two identifier parts",
)
.single()),
}
}
}
}
pub struct HamelinSimpleIdentifier {
pub ctx: Rc<SimpleIdentifierContextAll<'static>>,
}
impl HamelinSimpleIdentifier {
pub fn new(ctx: Rc<SimpleIdentifierContextAll<'static>>) -> Self {
Self { ctx }
}
pub fn to_sql(&self) -> Result<SimpleIdentifier, TranslationErrors> {
SimpleIdentifier::from_parse_tree(self.ctx.as_ref()).map_err(|x| x.into())
}
pub fn get_tuple_index(&self) -> Result<usize, TranslationError> {
let ctx = self.ctx.as_ref();
let text = ctx.get_text();
let msg = "Bad tuple reference. Must start with f and be followed by a number.";
if !text.starts_with("f") {
return Err(TranslationError::msg(ctx, msg));
}
text.get(1..)
.ok_or_else(|| TranslationError::msg(ctx, msg))
.and_then(|t| {
t.parse::<usize>()
.map_err(|_| TranslationError::msg(ctx, msg))
})
}
}
#[cfg(test)]
mod test {
use rstest::rstest;
use super::*;
#[rstest]
#[case("identifier", "identifier")]
#[case("`let`", "let")]
#[case("`let.foob`", "let.foob")]
pub fn test_simple_identifiers(#[case] hamelin: String, #[case] expected: String) {
match HamelinIdentifier::new(parse_identifier(hamelin.clone()).unwrap())
.to_sql()
.unwrap()
{
Identifier::Simple(simple_identifier) => {
assert_eq!(simple_identifier.name, expected)
}
Identifier::Compound(_) => panic!("should have been simple"),
}
assert_eq!(
HamelinSimpleIdentifier::new(parse_simple_identifier(hamelin).unwrap())
.to_sql()
.unwrap(),
SimpleIdentifier::new(expected.as_str())
);
}
#[rstest]
#[case("one.two", vec!["one", "two"])]
#[case("`let`.two", vec!["let", "two"])]
#[case("`let`.`select`.`two.three`.four", vec!["let", "select", "two.three", "four"])]
pub fn test_compound_identifiers(#[case] hamelin: String, #[case] expected: Vec<&str>) {
match HamelinIdentifier::new(parse_identifier(hamelin.clone()).unwrap())
.to_sql()
.unwrap()
{
Identifier::Simple(_) => panic!("should have been compound"),
Identifier::Compound(compound_identifier) => {
assert_eq!(
compound_identifier
.into_vec()
.into_iter()
.map(|s| s.name)
.collect::<Vec<_>>(),
expected
)
}
}
}
#[rstest]
#[case("one.two")]
#[case("select")]
pub fn test_simple_identifier_errors(#[case] hamelin: String) {
assert!(parse_simple_identifier(hamelin).is_err());
}
#[rstest]
#[case("select")]
pub fn test_identifier_errors(#[case] hamelin: String) {
assert!(parse_simple_identifier(hamelin).is_err());
}
}