use core::fmt;
use smallvec::SmallVec;
use sqlxo_traits::AliasedColumn;
use std::{
borrow::Cow,
fmt::{
Display,
Formatter,
},
};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct QualifiedColumn {
pub table_alias: String,
pub column: &'static str,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum SelectType {
Star,
StarWithExtras(SmallVec<[AliasedColumn; 4]>),
StarAndCount,
StarAndCountExtras(SmallVec<[AliasedColumn; 4]>),
Exists,
Columns(SmallVec<[QualifiedColumn; 4]>),
Projection(Vec<SelectProjection>),
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DeleteType {
Hard,
Soft,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SelectProjection {
pub expression: String,
pub alias: Option<String>,
}
pub trait ToHead {
fn to_head(self) -> Cow<'static, str>;
}
pub struct ReadHead<'a> {
r#type: SelectType,
table: &'a str,
}
impl<'a> ReadHead<'a> {
pub fn new(table: &'a str, r#type: SelectType) -> Self {
Self { r#type, table }
}
}
impl<'a> ToHead for ReadHead<'a> {
fn to_head(self) -> Cow<'static, str> {
self.to_string().into()
}
}
impl<'a> Display for ReadHead<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match &self.r#type {
SelectType::Star => {
write!(f, r#"SELECT "{}".* FROM {}"#, self.table, self.table)
}
SelectType::StarWithExtras(cols) => {
write!(f, r#"SELECT "{}".*"#, self.table)?;
write_extras(cols, f)?;
write!(f, " FROM {}", self.table)
}
SelectType::StarAndCount => {
write!(
f,
r#"SELECT "{}".*, COUNT(*) OVER() AS total_count FROM {}"#,
self.table, self.table
)
}
SelectType::StarAndCountExtras(cols) => {
write!(
f,
r#"SELECT "{}".*, COUNT(*) OVER() AS total_count"#,
self.table
)?;
write_extras(cols, f)?;
write!(f, " FROM {}", self.table)
}
SelectType::Exists => {
write!(f, "SELECT EXISTS(SELECT 1 FROM {}", self.table)
}
SelectType::Projection(exprs) => {
write!(f, "SELECT ")?;
for (idx, expr) in exprs.iter().enumerate() {
if idx > 0 {
write!(f, ", ")?;
}
write!(f, "{}", expr.expression)?;
if let Some(alias) = &expr.alias {
write!(f, r#" AS "{}""#, alias)?;
}
}
write!(f, " FROM {}", self.table)
}
SelectType::Columns(cols) => {
let mut first = true;
write!(f, "SELECT ")?;
for col in cols {
if !first {
write!(f, ", ")?;
}
first = false;
write!(f, r#""{}"."{}""#, col.table_alias, col.column)?;
}
write!(f, " FROM {}", self.table)
}
}
}
}
fn write_extras(cols: &[AliasedColumn], f: &mut Formatter<'_>) -> fmt::Result {
for col in cols {
write!(
f,
r#", "{}"."{}" AS "{}""#,
col.table_alias, col.column, col.alias
)?;
}
Ok(())
}
pub struct DeleteHead<'a> {
r#type: DeleteType,
table: &'a str,
delete_marker_field: Option<&'a str>,
}
impl<'a> DeleteHead<'a> {
pub fn new(
table: &'a str,
is_soft: bool,
delete_marker_field: Option<&'a str>,
) -> Self {
Self {
r#type: if is_soft {
DeleteType::Soft
} else {
DeleteType::Hard
},
table,
delete_marker_field,
}
}
}
impl<'a> ToHead for DeleteHead<'a> {
fn to_head(self) -> Cow<'static, str> {
self.to_string().into()
}
}
impl<'a> Display for DeleteHead<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match &self.r#type {
DeleteType::Hard => {
write!(f, "DELETE FROM {}", self.table)
}
DeleteType::Soft => {
let field = self
.delete_marker_field
.expect("Soft delete requires delete_marker_field");
write!(f, "UPDATE {} SET {} = NOW()", self.table, field)
}
}
}
}
pub struct UpdateHead<'a> {
table: &'a str,
}
impl<'a> UpdateHead<'a> {
pub fn new(table: &'a str) -> Self {
Self { table }
}
}
impl<'a> ToHead for UpdateHead<'a> {
fn to_head(self) -> Cow<'static, str> {
format!("UPDATE {} SET ", self.table).into()
}
}
pub struct InsertHead<'a> {
table: &'a str,
}
impl<'a> InsertHead<'a> {
pub fn new(table: &'a str) -> Self {
Self { table }
}
}
impl<'a> ToHead for InsertHead<'a> {
fn to_head(self) -> Cow<'static, str> {
format!("INSERT INTO {} ", self.table).into()
}
}