use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct IndexMeta(pub BTreeMap<String, IndexValue>);
impl IndexMeta {
pub fn meta(&self) -> &BTreeMap<String, IndexValue> {
&self.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum IndexValueInner {
String(String),
Int(i64),
Float(f64),
}
impl IndexValueInner {
pub fn as_string(&self) -> Option<&str> {
match self {
IndexValueInner::String(s) => Some(s),
_ => None,
}
}
pub fn as_int(&self) -> Option<i64> {
match self {
IndexValueInner::Int(i) => Some(*i),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
IndexValueInner::Float(f) => Some(*f),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum IndexValue {
String(String),
Int(i64),
Float(f64),
Bool(bool),
Uuid(Uuid),
Timestamp(chrono::DateTime<chrono::Utc>),
Array(Vec<IndexValueInner>),
}
impl IndexValue {
pub fn as_string(&self) -> Option<&str> {
match self {
IndexValue::String(s) => Some(s),
_ => None,
}
}
pub fn as_int(&self) -> Option<i64> {
match self {
IndexValue::Int(i) => Some(*i),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
IndexValue::Float(f) => Some(*f),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
IndexValue::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_timestamp(&self) -> Option<chrono::DateTime<chrono::Utc>> {
match self {
IndexValue::Timestamp(t) => Some(*t),
_ => None,
}
}
pub fn as_array(&self) -> Option<Vec<IndexValueInner>> {
match self {
IndexValue::Array(a) => Some(a.clone()),
_ => None,
}
}
}
pub trait ToIndexValue {
fn to_index_value(&self) -> IndexValue;
}
impl ToIndexValue for String {
fn to_index_value(&self) -> IndexValue {
IndexValue::String(self.clone())
}
}
impl ToIndexValue for &str {
fn to_index_value(&self) -> IndexValue {
IndexValue::String(self.to_string())
}
}
impl ToIndexValue for i64 {
fn to_index_value(&self) -> IndexValue {
IndexValue::Int(*self)
}
}
impl ToIndexValue for i32 {
fn to_index_value(&self) -> IndexValue {
IndexValue::Int(*self as i64)
}
}
impl ToIndexValue for f64 {
fn to_index_value(&self) -> IndexValue {
IndexValue::Float(*self)
}
}
impl ToIndexValue for f32 {
fn to_index_value(&self) -> IndexValue {
IndexValue::Float(*self as f64)
}
}
impl ToIndexValue for bool {
fn to_index_value(&self) -> IndexValue {
IndexValue::Bool(*self)
}
}
impl ToIndexValue for chrono::DateTime<chrono::Utc> {
fn to_index_value(&self) -> IndexValue {
IndexValue::Timestamp(*self)
}
}
impl ToIndexValue for IndexValueInner {
fn to_index_value(&self) -> IndexValue {
match self {
IndexValueInner::String(s) => IndexValue::String(s.clone()),
IndexValueInner::Int(i) => IndexValue::Int(*i),
IndexValueInner::Float(f) => IndexValue::Float(*f),
}
}
}
impl ToIndexValue for Vec<IndexValueInner> {
fn to_index_value(&self) -> IndexValue {
IndexValue::Array(self.clone())
}
}
impl ToIndexValue for Vec<String> {
fn to_index_value(&self) -> IndexValue {
IndexValue::Array(
self.iter()
.map(|s| IndexValueInner::String(s.clone()))
.collect(),
)
}
}
impl ToIndexValue for Vec<&str> {
fn to_index_value(&self) -> IndexValue {
IndexValue::Array(
self.iter()
.map(|s| IndexValueInner::String(s.to_string()))
.collect(),
)
}
}
impl ToIndexValue for Vec<i64> {
fn to_index_value(&self) -> IndexValue {
IndexValue::Array(self.iter().map(|i| IndexValueInner::Int(*i)).collect())
}
}
impl ToIndexValue for Vec<f64> {
fn to_index_value(&self) -> IndexValue {
IndexValue::Array(self.iter().map(|f| IndexValueInner::Float(*f)).collect())
}
}
impl ToIndexValue for Uuid {
fn to_index_value(&self) -> IndexValue {
IndexValue::Uuid(self.clone())
}
}
impl<T: ToIndexValue + Default> ToIndexValue for Option<T> {
fn to_index_value(&self) -> IndexValue {
match self {
Some(val) => val.to_index_value(),
None => T::default().to_index_value(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IndexKind {
Search, Sort, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IndexField {
pub name: &'static str,
pub kinds: &'static [IndexKind],
}
pub trait IndexQuery {
fn indexed_fields() -> &'static [IndexField];
}
#[derive(Debug, Clone)]
pub struct QueryFilter {
pub field: &'static IndexField,
pub value: IndexValue,
pub mode: QueryMode,
}
#[derive(Debug, Clone)]
pub enum QueryMode {
Search(QuerySearch),
Sort(QuerySort),
SortRandom,
}
impl QueryMode {
pub fn as_search(&self) -> Option<&QuerySearch> {
match self {
QueryMode::Search(search) => Some(search),
_ => None,
}
}
pub fn as_sort(&self) -> Option<&QuerySort> {
match self {
QueryMode::Sort(sort) => Some(sort),
_ => None,
}
}
pub fn is_random_sort(&self) -> bool {
matches!(self, QueryMode::SortRandom)
}
pub fn search(comp: Comparison, op: Option<Operator>) -> Self {
QueryMode::Search(QuerySearch {
comparison: comp,
operator: op.unwrap_or_default(),
})
}
pub fn search_default() -> Self {
QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::And,
})
}
pub fn sort(asc: bool) -> Self {
QueryMode::Sort(QuerySort { ascending: asc })
}
pub fn sort_default() -> Self {
QueryMode::Sort(QuerySort { ascending: true })
}
pub fn sort_random() -> Self {
QueryMode::SortRandom
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QuerySearch {
pub comparison: Comparison,
pub operator: Operator,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QuerySort {
pub ascending: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Comparison {
Equal,
NotEqual,
BeginsWith,
NotBeginsWith,
Contains,
NotContains,
ContainsAll,
NotContainsAll,
NotIn,
GreaterThan,
LessThan,
GreaterThanOrEqual,
LessThanOrEqual,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum Operator {
#[default]
And,
Or,
}
pub static SORT_RANDOM_FIELD: IndexField = IndexField {
name: "__random__",
kinds: &[],
};
#[derive(Debug, Clone, Copy)]
pub struct Cursor {
pub last_id: Uuid,
}
impl Into<Cursor> for Uuid {
fn into(self) -> Cursor {
Cursor { last_id: self }
}
}
impl QueryFilter {
pub fn random_sort() -> Self {
Self {
field: &SORT_RANDOM_FIELD,
value: IndexValue::Bool(false),
mode: QueryMode::SortRandom,
}
}
}