use crate::filter::{Filter, FilterValue, ValueList};
use std::borrow::Cow;
#[derive(Debug, Clone, PartialEq)]
pub struct StaticFilter {
inner: Filter,
}
impl StaticFilter {
#[inline]
pub const fn new(inner: Filter) -> Self {
Self { inner }
}
#[inline]
pub fn into_filter(self) -> Filter {
self.inner
}
#[inline]
pub fn as_filter(&self) -> &Filter {
&self.inner
}
}
impl From<StaticFilter> for Filter {
#[inline]
fn from(f: StaticFilter) -> Self {
f.inner
}
}
pub mod fields {
pub const ID: &str = "id";
pub const UUID: &str = "uuid";
pub const NAME: &str = "name";
pub const EMAIL: &str = "email";
pub const USERNAME: &str = "username";
pub const PASSWORD: &str = "password";
pub const TITLE: &str = "title";
pub const DESCRIPTION: &str = "description";
pub const CONTENT: &str = "content";
pub const BODY: &str = "body";
pub const STATUS: &str = "status";
pub const TYPE: &str = "type";
pub const ROLE: &str = "role";
pub const ACTIVE: &str = "active";
pub const ENABLED: &str = "enabled";
pub const DELETED: &str = "deleted";
pub const VERIFIED: &str = "verified";
pub const PUBLISHED: &str = "published";
pub const COUNT: &str = "count";
pub const SCORE: &str = "score";
pub const PRIORITY: &str = "priority";
pub const ORDER: &str = "order";
pub const POSITION: &str = "position";
pub const AGE: &str = "age";
pub const AMOUNT: &str = "amount";
pub const PRICE: &str = "price";
pub const QUANTITY: &str = "quantity";
pub const USER_ID: &str = "user_id";
pub const POST_ID: &str = "post_id";
pub const COMMENT_ID: &str = "comment_id";
pub const CATEGORY_ID: &str = "category_id";
pub const PARENT_ID: &str = "parent_id";
pub const AUTHOR_ID: &str = "author_id";
pub const OWNER_ID: &str = "owner_id";
pub const CREATED_AT: &str = "created_at";
pub const UPDATED_AT: &str = "updated_at";
pub const DELETED_AT: &str = "deleted_at";
pub const PUBLISHED_AT: &str = "published_at";
pub const EXPIRES_AT: &str = "expires_at";
pub const STARTS_AT: &str = "starts_at";
pub const ENDS_AT: &str = "ends_at";
pub const LAST_LOGIN_AT: &str = "last_login_at";
pub const VERIFIED_AT: &str = "verified_at";
pub const SLUG: &str = "slug";
pub const URL: &str = "url";
pub const PATH: &str = "path";
pub const KEY: &str = "key";
pub const VALUE: &str = "value";
pub const TOKEN: &str = "token";
pub const CODE: &str = "code";
pub const VERSION: &str = "version";
}
#[inline]
pub fn eq(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::Equals(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn ne(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::NotEquals(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn lt(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::Lt(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn lte(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::Lte(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn gt(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::Gt(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn gte(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::Gte(Cow::Borrowed(field), value.into())
}
#[inline]
pub const fn is_null(field: &'static str) -> Filter {
Filter::IsNull(Cow::Borrowed(field))
}
#[inline]
pub const fn is_not_null(field: &'static str) -> Filter {
Filter::IsNotNull(Cow::Borrowed(field))
}
#[inline]
pub fn contains(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::Contains(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn starts_with(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::StartsWith(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn ends_with(field: &'static str, value: impl Into<FilterValue>) -> Filter {
Filter::EndsWith(Cow::Borrowed(field), value.into())
}
#[inline]
pub fn in_list(field: &'static str, values: impl Into<ValueList>) -> Filter {
Filter::In(Cow::Borrowed(field), values.into())
}
#[inline]
pub fn not_in_list(field: &'static str, values: impl Into<ValueList>) -> Filter {
Filter::NotIn(Cow::Borrowed(field), values.into())
}
#[inline]
pub fn and2(a: Filter, b: Filter) -> Filter {
Filter::And(Box::new([a, b]))
}
#[inline]
pub fn and3(a: Filter, b: Filter, c: Filter) -> Filter {
Filter::And(Box::new([a, b, c]))
}
#[inline]
pub fn and4(a: Filter, b: Filter, c: Filter, d: Filter) -> Filter {
Filter::And(Box::new([a, b, c, d]))
}
#[inline]
pub fn and5(a: Filter, b: Filter, c: Filter, d: Filter, e: Filter) -> Filter {
Filter::And(Box::new([a, b, c, d, e]))
}
#[inline]
pub fn or2(a: Filter, b: Filter) -> Filter {
Filter::Or(Box::new([a, b]))
}
#[inline]
pub fn or3(a: Filter, b: Filter, c: Filter) -> Filter {
Filter::Or(Box::new([a, b, c]))
}
#[inline]
pub fn or4(a: Filter, b: Filter, c: Filter, d: Filter) -> Filter {
Filter::Or(Box::new([a, b, c, d]))
}
#[inline]
pub fn or5(a: Filter, b: Filter, c: Filter, d: Filter, e: Filter) -> Filter {
Filter::Or(Box::new([a, b, c, d, e]))
}
#[inline]
pub fn not(filter: Filter) -> Filter {
Filter::Not(Box::new(filter))
}
#[derive(Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum CompactValue {
Null = 0,
True = 1,
False = 2,
SmallInt(i8) = 3,
Int(i64) = 4,
Float(f64) = 5,
String(String) = 6,
}
impl CompactValue {
#[inline]
pub fn into_filter_value(self) -> FilterValue {
match self {
Self::Null => FilterValue::Null,
Self::True => FilterValue::Bool(true),
Self::False => FilterValue::Bool(false),
Self::SmallInt(v) => FilterValue::Int(v as i64),
Self::Int(v) => FilterValue::Int(v),
Self::Float(v) => FilterValue::Float(v),
Self::String(v) => FilterValue::String(v),
}
}
}
impl From<bool> for CompactValue {
#[inline]
fn from(v: bool) -> Self {
if v { Self::True } else { Self::False }
}
}
impl From<i32> for CompactValue {
#[inline]
fn from(v: i32) -> Self {
if (-128..=127).contains(&v) {
Self::SmallInt(v as i8)
} else {
Self::Int(v as i64)
}
}
}
impl From<i64> for CompactValue {
#[inline]
fn from(v: i64) -> Self {
if (-128..=127).contains(&v) {
Self::SmallInt(v as i8)
} else {
Self::Int(v)
}
}
}
impl From<f64> for CompactValue {
#[inline]
fn from(v: f64) -> Self {
Self::Float(v)
}
}
impl From<String> for CompactValue {
#[inline]
fn from(v: String) -> Self {
Self::String(v)
}
}
impl From<&str> for CompactValue {
#[inline]
fn from(v: &str) -> Self {
Self::String(v.to_string())
}
}
impl From<CompactValue> for FilterValue {
#[inline]
fn from(v: CompactValue) -> Self {
v.into_filter_value()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_eq_filter() {
let filter = eq(fields::ID, 42);
assert!(matches!(filter, Filter::Equals(_, FilterValue::Int(42))));
}
#[test]
fn test_gt_filter() {
let filter = gt(fields::AGE, 18);
assert!(matches!(filter, Filter::Gt(_, FilterValue::Int(18))));
}
#[test]
fn test_is_null_filter() {
let filter = is_null(fields::DELETED_AT);
assert!(matches!(filter, Filter::IsNull(_)));
}
#[test]
fn test_and2_filter() {
let filter = and2(eq(fields::ACTIVE, true), gt(fields::SCORE, 100));
assert!(matches!(filter, Filter::And(_)));
}
#[test]
fn test_or2_filter() {
let filter = or2(eq(fields::STATUS, "active"), eq(fields::STATUS, "pending"));
assert!(matches!(filter, Filter::Or(_)));
}
#[test]
fn test_compact_value_bool() {
let v: CompactValue = true.into();
assert!(matches!(v, CompactValue::True));
assert_eq!(v.into_filter_value(), FilterValue::Bool(true));
}
#[test]
fn test_compact_value_small_int() {
let v: CompactValue = 42i32.into();
assert!(matches!(v, CompactValue::SmallInt(42)));
}
#[test]
fn test_compact_value_large_int() {
let v: CompactValue = 1000i32.into();
assert!(matches!(v, CompactValue::Int(1000)));
}
#[test]
fn test_field_constants() {
assert_eq!(fields::ID, "id");
assert_eq!(fields::EMAIL, "email");
assert_eq!(fields::CREATED_AT, "created_at");
}
}