use std::fmt::{self, Display};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Operator {
Known(&'static str),
Unknown(String),
}
impl Operator {
pub const GT: Self = Operator::Known(">");
pub const LT: Self = Operator::Known("<");
pub const EQ: Self = Operator::Known("=");
pub const NEQ: Self = Operator::Known("!=");
pub const GTE: Self = Operator::Known(">=");
pub const LTE: Self = Operator::Known("<=");
pub const LIKE: Self = Operator::Known("LIKE");
pub const ILIKE: Self = Operator::Known("ILIKE");
pub const IN: Self = Operator::Known("IN");
pub const NOT_IN: Self = Operator::Known("NOT IN");
pub const IS_NULL: Self = Operator::Known("IS NULL");
pub const IS_NOT_NULL: Self = Operator::Known("IS NOT NULL");
pub const EXISTS: Self = Operator::Known("EXISTS");
pub const NOT_EXISTS: Self = Operator::Known("NOT EXISTS");
pub const fn custom(op: &'static str) -> Self {
Operator::Known(op)
}
pub fn as_str(&self) -> &str {
match self {
Operator::Known(op) => op,
Operator::Unknown(op) => op,
}
}
pub fn validate(&self) -> crate::Result<()> {
match self {
Operator::Known(_) => Ok(()),
Operator::Unknown(op) => {
Err(crate::Error::invalid_query(format!(
"Unknown operator '{}'. Use Operator::{} constants or Operator::custom(\"{}\") for custom operators.",
op,
op.to_uppercase().replace(" ", "_").replace("!", "N"),
op
)))
}
}
}
}
impl Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub trait IntoOperator {
fn into_operator(self) -> Operator;
}
impl IntoOperator for Operator {
fn into_operator(self) -> Operator {
self
}
}
impl IntoOperator for &str {
fn into_operator(self) -> Operator {
match self {
">" => Operator::GT,
"<" => Operator::LT,
"=" => Operator::EQ,
"!=" => Operator::NEQ,
">=" => Operator::GTE,
"<=" => Operator::LTE,
"LIKE" | "like" => Operator::LIKE,
"ILIKE" | "ilike" => Operator::ILIKE,
"IN" | "in" => Operator::IN,
"NOT IN" | "not in" => Operator::NOT_IN,
"IS NULL" | "is null" => Operator::IS_NULL,
"IS NOT NULL" | "is not null" => Operator::IS_NOT_NULL,
"EXISTS" | "exists" => Operator::EXISTS,
"NOT EXISTS" | "not exists" => Operator::NOT_EXISTS,
_ => Operator::Unknown(self.to_string()),
}
}
}
pub mod op {
use super::Operator;
pub const GT: Operator = Operator::GT;
pub const LT: Operator = Operator::LT;
pub const EQ: Operator = Operator::EQ;
pub const NEQ: Operator = Operator::NEQ;
pub const GTE: Operator = Operator::GTE;
pub const LTE: Operator = Operator::LTE;
pub const LIKE: Operator = Operator::LIKE;
pub const ILIKE: Operator = Operator::ILIKE;
pub const IN: Operator = Operator::IN;
pub const NOT_IN: Operator = Operator::NOT_IN;
pub const IS_NULL: Operator = Operator::IS_NULL;
pub const IS_NOT_NULL: Operator = Operator::IS_NOT_NULL;
pub const EXISTS: Operator = Operator::EXISTS;
pub const NOT_EXISTS: Operator = Operator::NOT_EXISTS;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_operator_constants() {
assert_eq!(Operator::GT.as_str(), ">");
assert_eq!(Operator::LT.as_str(), "<");
assert_eq!(Operator::EQ.as_str(), "=");
assert_eq!(Operator::LIKE.as_str(), "LIKE");
}
#[test]
fn test_custom_operator() {
let custom_op = Operator::custom("@@");
assert_eq!(custom_op.as_str(), "@@");
}
#[test]
fn test_display() {
assert_eq!(format!("{}", Operator::GT), ">");
assert_eq!(format!("{}", Operator::LIKE), "LIKE");
}
#[test]
fn test_string_conversion() {
assert_eq!(">".into_operator(), Operator::GT);
assert_eq!("LIKE".into_operator(), Operator::LIKE);
assert_eq!("like".into_operator(), Operator::LIKE);
assert_eq!(">=".into_operator(), Operator::GTE);
}
#[test]
fn test_invalid_string_conversion() {
let invalid_op = "INVALID".into_operator();
assert_eq!(invalid_op, Operator::Unknown("INVALID".to_string()));
assert!(invalid_op.validate().is_err());
}
#[test]
fn test_operator_equality() {
assert_eq!(Operator::GT, ">".into_operator());
assert_eq!(Operator::LIKE, "like".into_operator());
}
#[test]
fn test_null_operators() {
assert_eq!("IS NULL".into_operator(), Operator::IS_NULL);
assert_eq!("is null".into_operator(), Operator::IS_NULL);
assert_eq!("IS NOT NULL".into_operator(), Operator::IS_NOT_NULL);
}
#[test]
fn test_deferred_validation_in_query() {
use crate::{builder::common::QueryBuilder, from};
let query = from("users").select("*").where_(("age", "INVALID_OP", 18));
assert!(query.to_sql().is_err());
let err = query.to_sql().unwrap_err();
assert!(err.to_string().contains("Unknown operator 'INVALID_OP'"));
}
}