use std::marker::PhantomData;
mod impls;
pub trait IntoColumnName {
fn column_name(&self) -> &str;
}
impl IntoColumnName for &str {
fn column_name(&self) -> &str {
self
}
}
impl IntoColumnName for String {
fn column_name(&self) -> &str {
self.as_str()
}
}
impl IntoColumnName for &String {
fn column_name(&self) -> &str {
self.as_str()
}
}
impl<T> IntoColumnName for Column<T> {
fn column_name(&self) -> &str {
self.name
}
}
#[derive(Debug, Clone, Copy)]
pub struct Column<T> {
name: &'static str,
_phantom: PhantomData<T>,
}
impl<T> Column<T> {
pub const fn new(name: &'static str) -> Self {
Self {
name,
_phantom: PhantomData,
}
}
pub const fn name(&self) -> &'static str {
self.name
}
}
#[derive(Debug, Clone)]
pub struct ColumnCondition {
pub column: String,
pub operator: ColumnOperator,
pub value: serde_json::Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColumnOperator {
Eq,
NotEq,
Gt,
Gte,
Lt,
Lte,
Like,
LikeEscaped,
NotLike,
In,
NotIn,
IsNull,
IsNotNull,
Between,
}
impl ColumnOperator {
pub fn to_sql(&self) -> &'static str {
match self {
Self::Eq => "=",
Self::NotEq => "<>",
Self::Gt => ">",
Self::Gte => ">=",
Self::Lt => "<",
Self::Lte => "<=",
Self::Like => "LIKE",
Self::LikeEscaped => "LIKE",
Self::NotLike => "NOT LIKE",
Self::In => "IN",
Self::NotIn => "NOT IN",
Self::IsNull => "IS NULL",
Self::IsNotNull => "IS NOT NULL",
Self::Between => "BETWEEN",
}
}
}
pub(crate) fn escape_like_literal(value: &str) -> String {
let mut escaped = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'\\' | '%' | '_' => {
escaped.push('\\');
escaped.push(ch);
}
_ => escaped.push(ch),
}
}
escaped
}
pub trait ColumnEq<T> {
fn eq(self, value: T) -> ColumnCondition;
fn ne(self, value: T) -> ColumnCondition;
}
pub trait ColumnOrd<T>: ColumnEq<T> {
fn gt(self, value: T) -> ColumnCondition;
fn gte(self, value: T) -> ColumnCondition;
fn lt(self, value: T) -> ColumnCondition;
fn lte(self, value: T) -> ColumnCondition;
fn between(self, low: T, high: T) -> ColumnCondition;
}
pub trait ColumnLike {
fn like(self, pattern: &str) -> ColumnCondition;
fn not_like(self, pattern: &str) -> ColumnCondition;
fn contains(self, substr: &str) -> ColumnCondition;
fn starts_with(self, prefix: &str) -> ColumnCondition;
fn ends_with(self, suffix: &str) -> ColumnCondition;
}
#[allow(clippy::wrong_self_convention)]
pub trait ColumnNullable {
fn is_null(self) -> ColumnCondition;
fn is_not_null(self) -> ColumnCondition;
}
#[allow(clippy::wrong_self_convention)]
pub trait ColumnIn<T> {
fn is_in(self, values: Vec<T>) -> ColumnCondition;
fn not_in(self, values: Vec<T>) -> ColumnCondition;
}
#[cfg(test)]
#[path = "../tests/unit/columns_tests.rs"]
mod tests;