use crate::{IntoOperator, Operator, Result, Value};
pub trait QueryBuilder {
fn to_sql(&self) -> Result<String>;
fn parameters(&self) -> &[Value];
fn clone_builder(&self) -> Self
where
Self: Sized;
}
pub trait IntoCondition {
fn into_condition(self) -> (String, Operator, Value);
}
impl<T> IntoCondition for (&str, T)
where
T: Into<Value>,
{
fn into_condition(self) -> (String, Operator, Value) {
(self.0.to_string(), Operator::EQ, self.1.into())
}
}
impl<T, O> IntoCondition for (&str, O, T)
where
T: Into<Value>,
O: IntoOperator,
{
fn into_condition(self) -> (String, Operator, Value) {
(self.0.to_string(), self.1.into_operator(), self.2.into())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WhereCondition {
pub column: String,
pub operator: Operator,
pub value: Value,
pub connector: WhereConnector,
}
#[derive(Debug, Clone, PartialEq)]
pub enum WhereConnector {
And,
Or,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AggregateFunction {
Count,
CountDistinct,
Sum,
Avg,
Min,
Max,
}
impl std::fmt::Display for AggregateFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AggregateFunction::Count => write!(f, "COUNT"),
AggregateFunction::CountDistinct => write!(f, "COUNT(DISTINCT"),
AggregateFunction::Sum => write!(f, "SUM"),
AggregateFunction::Avg => write!(f, "AVG"),
AggregateFunction::Min => write!(f, "MIN"),
AggregateFunction::Max => write!(f, "MAX"),
}
}
}
#[derive(Debug, Clone)]
pub enum ColumnSelector {
Column(String),
Aggregate {
function: AggregateFunction,
column: String,
alias: Option<String>,
},
CountAll {
alias: Option<String>,
},
}
impl ColumnSelector {
pub fn count() -> Self {
Self::CountAll { alias: None }
}
pub fn count_as(alias: &str) -> Self {
Self::CountAll {
alias: Some(alias.to_string()),
}
}
pub fn count_column(column: &str) -> Self {
Self::Aggregate {
function: AggregateFunction::Count,
column: column.to_string(),
alias: None,
}
}
pub fn count_distinct(column: &str) -> Self {
Self::Aggregate {
function: AggregateFunction::CountDistinct,
column: column.to_string(),
alias: None,
}
}
pub fn sum(column: &str) -> Self {
Self::Aggregate {
function: AggregateFunction::Sum,
column: column.to_string(),
alias: None,
}
}
pub fn avg(column: &str) -> Self {
Self::Aggregate {
function: AggregateFunction::Avg,
column: column.to_string(),
alias: None,
}
}
pub fn min(column: &str) -> Self {
Self::Aggregate {
function: AggregateFunction::Min,
column: column.to_string(),
alias: None,
}
}
pub fn max(column: &str) -> Self {
Self::Aggregate {
function: AggregateFunction::Max,
column: column.to_string(),
alias: None,
}
}
pub fn as_alias(mut self, alias: &str) -> Self {
match self {
Self::Column(_) => {
self
}
Self::Aggregate {
alias: ref mut alias_field,
..
} => {
*alias_field = Some(alias.to_string());
self
}
Self::CountAll {
alias: ref mut alias_field,
} => {
*alias_field = Some(alias.to_string());
self
} }
}
}
pub trait IntoColumns {
fn into_columns(self) -> Vec<String>;
}
impl IntoColumns for &str {
fn into_columns(self) -> Vec<String> {
vec![self.to_string()]
}
}
impl IntoColumns for String {
fn into_columns(self) -> Vec<String> {
vec![self]
}
}
impl IntoColumns for Vec<String> {
fn into_columns(self) -> Vec<String> {
self
}
}
impl IntoColumns for Vec<&str> {
fn into_columns(self) -> Vec<String> {
self.into_iter().map(|s| s.to_string()).collect()
}
}
impl IntoColumns for (&str, &str) {
fn into_columns(self) -> Vec<String> {
vec![self.0.to_string(), self.1.to_string()]
}
}
impl IntoColumns for (&str, &str, &str) {
fn into_columns(self) -> Vec<String> {
vec![self.0.to_string(), self.1.to_string(), self.2.to_string()]
}
}
impl IntoColumns for (&str, &str, &str, &str) {
fn into_columns(self) -> Vec<String> {
vec![
self.0.to_string(),
self.1.to_string(),
self.2.to_string(),
self.3.to_string(),
]
}
}
impl IntoColumns for (&str, &str, &str, &str, &str) {
fn into_columns(self) -> Vec<String> {
vec![
self.0.to_string(),
self.1.to_string(),
self.2.to_string(),
self.3.to_string(),
self.4.to_string(),
]
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinType {
Inner,
Left,
Right,
Full,
Cross,
}
impl std::fmt::Display for JoinType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
JoinType::Inner => write!(f, "INNER"),
JoinType::Left => write!(f, "LEFT"),
JoinType::Right => write!(f, "RIGHT"),
JoinType::Full => write!(f, "FULL OUTER"),
JoinType::Cross => write!(f, "CROSS"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinConnector {
And,
Or,
}
#[derive(Debug, Clone, PartialEq)]
pub struct JoinCondition {
pub left_column: String,
pub operator: Operator,
pub right_column: String,
pub connector: JoinConnector,
}
#[derive(Debug, Clone, PartialEq)]
pub struct JoinClause {
pub join_type: JoinType,
pub table: String,
pub on_conditions: Vec<JoinCondition>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SortDirection {
Asc,
Desc,
}
impl std::fmt::Display for SortDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SortDirection::Asc => write!(f, "ASC"),
SortDirection::Desc => write!(f, "DESC"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct OrderByClause {
pub column: String,
pub direction: SortDirection,
}
#[derive(Debug, Clone, PartialEq)]
pub struct GroupByClause {
pub columns: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HavingCondition {
pub column_or_function: String,
pub operator: Operator,
pub value: Value,
pub connector: WhereConnector,
}
pub trait IntoColumnSelectors {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector>;
}
impl IntoColumnSelectors for &str {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![crate::ColumnSelector::Column(self.to_string())]
}
}
impl IntoColumnSelectors for String {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![crate::ColumnSelector::Column(self)]
}
}
impl IntoColumnSelectors for Vec<String> {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
self.into_iter()
.map(|s| crate::ColumnSelector::Column(s))
.collect()
}
}
impl IntoColumnSelectors for Vec<&str> {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
self.into_iter()
.map(|s| crate::ColumnSelector::Column(s.to_string()))
.collect()
}
}
impl IntoColumnSelectors for crate::ColumnSelector {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![self]
}
}
impl IntoColumnSelectors for Vec<crate::ColumnSelector> {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
self
}
}
impl IntoColumnSelectors for (&str, &str) {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![
crate::ColumnSelector::Column(self.0.to_string()),
crate::ColumnSelector::Column(self.1.to_string()),
]
}
}
impl IntoColumnSelectors for (&str, &str, &str) {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![
crate::ColumnSelector::Column(self.0.to_string()),
crate::ColumnSelector::Column(self.1.to_string()),
crate::ColumnSelector::Column(self.2.to_string()),
]
}
}
impl IntoColumnSelectors for (&str, &str, &str, &str) {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![
crate::ColumnSelector::Column(self.0.to_string()),
crate::ColumnSelector::Column(self.1.to_string()),
crate::ColumnSelector::Column(self.2.to_string()),
crate::ColumnSelector::Column(self.3.to_string()),
]
}
}
impl IntoColumnSelectors for (&str, &str, &str, &str, &str) {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![
crate::ColumnSelector::Column(self.0.to_string()),
crate::ColumnSelector::Column(self.1.to_string()),
crate::ColumnSelector::Column(self.2.to_string()),
crate::ColumnSelector::Column(self.3.to_string()),
crate::ColumnSelector::Column(self.4.to_string()),
]
}
}
impl IntoColumnSelectors for (&str, crate::ColumnSelector) {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![crate::ColumnSelector::Column(self.0.to_string()), self.1]
}
}
impl IntoColumnSelectors for (&str, crate::ColumnSelector, crate::ColumnSelector) {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![
crate::ColumnSelector::Column(self.0.to_string()),
self.1,
self.2,
]
}
}
impl IntoColumnSelectors for (crate::ColumnSelector, &str, crate::ColumnSelector) {
fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
vec![
self.0,
crate::ColumnSelector::Column(self.1.to_string()),
self.2,
]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::operator::op;
#[test]
fn test_string_operator_conversion() {
let condition = ("age", ">", 18);
let (column, operator, value) = condition.into_condition();
assert_eq!(column, "age");
assert_eq!(operator, op::GT);
assert_eq!(value, 18.into());
}
#[test]
fn test_condition_trait_implementations() {
let condition = ("name", "John");
let (column, operator, value) = condition.into_condition();
assert_eq!(column, "name");
assert_eq!(operator, op::EQ);
assert_eq!(value, "John".into());
let condition = ("age", op::GT, 18);
let (column, operator, value) = condition.into_condition();
assert_eq!(column, "age");
assert_eq!(operator, op::GT);
assert_eq!(value, 18.into());
}
#[test]
fn test_into_columns_implementations() {
let cols = "name".into_columns();
assert_eq!(cols, vec!["name"]);
let cols = ("name", "age").into_columns();
assert_eq!(cols, vec!["name", "age"]);
let cols = vec!["name", "age"].into_columns();
assert_eq!(cols, vec!["name", "age"]);
}
}