paginator-utils 0.2.2

Shared pagination response structures
Documentation
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum FilterOperator {
    Eq,
    Ne,
    Gt,
    Lt,
    Gte,
    Lte,
    Like,
    ILike,
    In,
    NotIn,
    IsNull,
    IsNotNull,
    Between,
    Contains,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum FilterValue {
    String(String),
    Int(i64),
    Float(f64),
    Bool(bool),
    Array(Vec<FilterValue>),
    Null,
}

impl FilterValue {
    pub fn to_sql_string(&self) -> String {
        match self {
            FilterValue::String(s) => format!("'{}'", s.replace('\'', "''")),
            FilterValue::Int(i) => i.to_string(),
            FilterValue::Float(f) => f.to_string(),
            FilterValue::Bool(b) => if *b { "TRUE" } else { "FALSE" }.to_string(),
            FilterValue::Array(arr) => {
                let items: Vec<String> = arr.iter().map(|v| v.to_sql_string()).collect();
                format!("({})", items.join(", "))
            }
            FilterValue::Null => "NULL".to_string(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Filter {
    pub field: String,
    pub operator: FilterOperator,
    pub value: FilterValue,
}

impl Filter {
    pub fn new(field: impl Into<String>, operator: FilterOperator, value: FilterValue) -> Self {
        Self {
            field: field.into(),
            operator,
            value,
        }
    }

    pub fn to_sql_where(&self) -> String {
        match &self.operator {
            FilterOperator::Eq => format!("{} = {}", self.field, self.value.to_sql_string()),
            FilterOperator::Ne => format!("{} != {}", self.field, self.value.to_sql_string()),
            FilterOperator::Gt => format!("{} > {}", self.field, self.value.to_sql_string()),
            FilterOperator::Lt => format!("{} < {}", self.field, self.value.to_sql_string()),
            FilterOperator::Gte => format!("{} >= {}", self.field, self.value.to_sql_string()),
            FilterOperator::Lte => format!("{} <= {}", self.field, self.value.to_sql_string()),
            FilterOperator::Like => format!("{} LIKE {}", self.field, self.value.to_sql_string()),
            FilterOperator::ILike => format!("{} ILIKE {}", self.field, self.value.to_sql_string()),
            FilterOperator::In => format!("{} IN {}", self.field, self.value.to_sql_string()),
            FilterOperator::NotIn => {
                format!("{} NOT IN {}", self.field, self.value.to_sql_string())
            }
            FilterOperator::IsNull => format!("{} IS NULL", self.field),
            FilterOperator::IsNotNull => format!("{} IS NOT NULL", self.field),
            FilterOperator::Between => {
                if let FilterValue::Array(arr) = &self.value {
                    if arr.len() == 2 {
                        return format!(
                            "{} BETWEEN {} AND {}",
                            self.field,
                            arr[0].to_sql_string(),
                            arr[1].to_sql_string()
                        );
                    }
                }
                format!("{} = {}", self.field, self.value.to_sql_string())
            }
            FilterOperator::Contains => {
                format!("{} @> {}", self.field, self.value.to_sql_string())
            }
        }
    }

    pub fn to_surrealql_where(&self) -> String {
        match &self.operator {
            FilterOperator::Eq => format!("{} = {}", self.field, self.value.to_sql_string()),
            FilterOperator::Ne => format!("{} != {}", self.field, self.value.to_sql_string()),
            FilterOperator::Gt => format!("{} > {}", self.field, self.value.to_sql_string()),
            FilterOperator::Lt => format!("{} < {}", self.field, self.value.to_sql_string()),
            FilterOperator::Gte => format!("{} >= {}", self.field, self.value.to_sql_string()),
            FilterOperator::Lte => format!("{} <= {}", self.field, self.value.to_sql_string()),
            FilterOperator::Like | FilterOperator::ILike => {
                format!("{} ~ {}", self.field, self.value.to_sql_string())
            }
            FilterOperator::In => format!("{} INSIDE {}", self.field, self.value.to_sql_string()),
            FilterOperator::NotIn => {
                format!("{} NOT INSIDE {}", self.field, self.value.to_sql_string())
            }
            FilterOperator::IsNull => format!("{} IS NULL", self.field),
            FilterOperator::IsNotNull => format!("{} IS NOT NULL", self.field),
            FilterOperator::Between => {
                if let FilterValue::Array(arr) = &self.value {
                    if arr.len() == 2 {
                        return format!(
                            "{} >= {} AND {} <= {}",
                            self.field,
                            arr[0].to_sql_string(),
                            self.field,
                            arr[1].to_sql_string()
                        );
                    }
                }
                format!("{} = {}", self.field, self.value.to_sql_string())
            }
            FilterOperator::Contains => {
                format!("{} CONTAINS {}", self.field, self.value.to_sql_string())
            }
        }
    }
}