use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Write as _;
use derive_visitor::Drive;
use derive_visitor::DriveMut;
use ethnum::i256;
use crate::ast::quote::QuotedIdent;
use crate::Span;
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub struct Identifier {
pub span: Span,
pub name: String,
pub quote: Option<char>,
#[drive(skip)]
pub ident_type: IdentifierType,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum IdentifierType {
#[default]
None,
Hole,
Variable,
}
impl Identifier {
pub fn is_quoted(&self) -> bool {
self.quote.is_some()
}
pub fn is_hole(&self) -> bool {
self.ident_type == IdentifierType::Hole
}
pub fn is_variable(&self) -> bool {
self.ident_type == IdentifierType::Variable
}
pub fn from_name(span: Span, name: impl Into<String>) -> Self {
Self {
span,
name: name.into(),
quote: None,
ident_type: IdentifierType::None,
}
}
pub fn from_name_with_quoted(span: Span, name: impl Into<String>, quote: Option<char>) -> Self {
Self {
span,
name: name.into(),
quote,
ident_type: IdentifierType::None,
}
}
}
impl Display for Identifier {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
if self.is_hole() {
write!(f, "IDENTIFIER(:{})", self.name)
} else if self.is_variable() {
write!(f, "IDENTIFIER(${})", self.name)
} else if let Some(quote) = self.quote {
write!(f, "{}", QuotedIdent(&self.name, quote))
} else {
write!(f, "{}", self.name)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub struct ColumnPosition {
pub span: Span,
pub pos: usize,
pub name: String,
}
impl ColumnPosition {
pub fn create(span: Span, pos: usize) -> ColumnPosition {
ColumnPosition {
pos,
name: format!("${}", pos),
span,
}
}
pub fn name(&self) -> String {
format!("${}", self.pos)
}
}
impl Display for ColumnPosition {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "${}", self.pos)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum ColumnID {
Name(Identifier),
Position(ColumnPosition),
}
impl ColumnID {
pub fn name(&self) -> &str {
match self {
ColumnID::Name(id) => &id.name,
ColumnID::Position(id) => &id.name,
}
}
}
impl Display for ColumnID {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
ColumnID::Name(id) => write!(f, "{}", id),
ColumnID::Position(id) => write!(f, "{}", id),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub struct DatabaseRef {
pub catalog: Option<Identifier>,
pub database: Identifier,
}
impl Display for DatabaseRef {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
if let Some(catalog) = &self.catalog {
write!(f, "{}.", catalog)?;
}
write!(f, "{}", self.database)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub struct TableRef {
pub catalog: Option<Identifier>,
pub database: Option<Identifier>,
pub table: Identifier,
}
impl Display for TableRef {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
assert!(self.catalog.is_none() || (self.catalog.is_some() && self.database.is_some()));
if let Some(catalog) = &self.catalog {
write!(f, "{}.", catalog)?;
}
if let Some(database) = &self.database {
write!(f, "{}.", database)?;
}
write!(f, "{}", self.table)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub struct DictionaryRef {
pub catalog: Option<Identifier>,
pub database: Option<Identifier>,
pub dictionary_name: Identifier,
}
impl Display for DictionaryRef {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
assert!(self.catalog.is_none() || (self.catalog.is_some() && self.database.is_some()));
if let Some(catalog) = &self.catalog {
write!(f, "{}.", catalog)?;
}
if let Some(database) = &self.database {
write!(f, "{}.", database)?;
}
write!(f, "{}", self.dictionary_name)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub struct ColumnRef {
pub database: Option<Identifier>,
pub table: Option<Identifier>,
pub column: ColumnID,
}
impl Display for ColumnRef {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
assert!(self.database.is_none() || (self.database.is_some() && self.table.is_some()));
if f.alternate() {
write!(f, "{}", self.column)?;
return Ok(());
}
if let Some(database) = &self.database {
write!(f, "{}.", database)?;
}
if let Some(table) = &self.table {
write!(f, "{}.", table)?;
}
write!(f, "{}", self.column)?;
Ok(())
}
}
pub(crate) fn write_dot_separated_list(
f: &mut Formatter,
items: impl IntoIterator<Item = impl Display>,
) -> std::fmt::Result {
for (i, item) in items.into_iter().enumerate() {
if i > 0 {
write!(f, ".")?;
}
write!(f, "{}", item)?;
}
Ok(())
}
pub(crate) fn write_comma_separated_list(
f: &mut Formatter,
items: impl IntoIterator<Item = impl Display>,
) -> std::fmt::Result {
for (i, item) in items.into_iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{item}")?;
}
Ok(())
}
pub(crate) fn write_comma_separated_string_list(
f: &mut Formatter,
items: impl IntoIterator<Item = impl Display>,
) -> std::fmt::Result {
for (i, item) in items.into_iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "'{item}'")?;
}
Ok(())
}
pub(crate) fn write_comma_separated_map(
f: &mut Formatter,
items: impl IntoIterator<Item = (impl Display, impl Display)>,
) -> std::fmt::Result {
for (i, (k, v)) in items.into_iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{k} = {v}")?;
}
Ok(())
}
pub(crate) fn write_comma_separated_string_map(
f: &mut Formatter,
items: impl IntoIterator<Item = (impl Display, impl Display)>,
) -> std::fmt::Result {
for (i, (k, v)) in items.into_iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{k} = '{v}'")?;
}
Ok(())
}
pub(crate) fn write_space_separated_string_map(
f: &mut Formatter,
items: impl IntoIterator<Item = (impl Display, impl Display)>,
) -> std::fmt::Result {
for (i, (k, v)) in items.into_iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{k} = '{v}'")?;
}
Ok(())
}
pub fn display_decimal_256(num: i256, scale: u8) -> String {
let mut buf = String::new();
if scale == 0 {
write!(buf, "{}", num).unwrap();
} else {
let pow_scale = i256::from(10).pow(scale as u32);
if num >= 0 {
write!(
buf,
"{}.{:0>width$}",
num / pow_scale,
(num % pow_scale).abs(),
width = scale as usize
)
.unwrap();
} else {
write!(
buf,
"-{}.{:0>width$}",
-num / pow_scale,
(num % pow_scale).abs(),
width = scale as usize
)
.unwrap();
}
}
buf
}