use std::marker::PhantomData;
use serde_json::{Map, Value};
use super::{
Common, FlussoValue, RangeRelation, Sort, SortOrder, Sortable, common_opts, exists_q, kind,
single, wrap,
};
use crate::FlussoDocument;
use crate::query::{AsQuery, Query, Root};
fn date_value(value: &impl FlussoValue<kind::Date>) -> Value {
match serde_json::to_value(value) {
Ok(Value::String(string)) => Value::String(string),
Ok(other) => Value::String(other.to_string()),
Err(_) => Value::String(String::new()),
}
}
fn number_value<K>(value: &impl FlussoValue<K>) -> Value {
match serde_json::to_value(value) {
Ok(Value::String(string)) => string
.parse::<serde_json::Number>()
.map_or(Value::String(string), Value::Number),
Ok(other) => other,
Err(_) => Value::Null,
}
}
fn bool_value(value: &impl FlussoValue<kind::Bool>) -> Value {
serde_json::to_value(value).unwrap_or(Value::Null)
}
#[derive(Debug, Clone)]
pub struct EqQuery<S = Root> {
path: String,
value: Value,
common: Common,
_scope: PhantomData<fn() -> S>,
}
impl<S> EqQuery<S> {
fn new(path: &str, value: Value) -> Self {
Self {
path: path.to_string(),
value,
common: Common::default(),
_scope: PhantomData,
}
}
common_opts!(common);
}
impl<S> AsQuery<S> for EqQuery<S> {
fn into_query(self) -> Option<Query<S>> {
if self.common.is_empty() {
Some(single("term", &self.path, self.value))
} else {
let mut body = Map::new();
body.insert("value".to_string(), self.value);
self.common.write(&mut body);
Some(single("term", &self.path, Value::Object(body)))
}
}
}
#[derive(Debug, Clone)]
pub struct TermsQuery<S = Root> {
path: String,
values: Vec<Value>,
common: Common,
_scope: PhantomData<fn() -> S>,
}
impl<S> TermsQuery<S> {
pub(crate) fn new(path: &str, values: Vec<Value>) -> Self {
Self {
path: path.to_string(),
values,
common: Common::default(),
_scope: PhantomData,
}
}
common_opts!(common);
}
impl<S> AsQuery<S> for TermsQuery<S> {
fn into_query(self) -> Option<Query<S>> {
let mut body = Map::new();
body.insert(self.path, Value::Array(self.values));
self.common.write(&mut body);
Some(wrap("terms", body))
}
}
#[derive(Debug, Clone)]
pub struct RangeQuery<S = Root> {
path: String,
bounds: Vec<(&'static str, Value)>,
extra: Map<String, Value>,
common: Common,
_scope: PhantomData<fn() -> S>,
}
impl<S> RangeQuery<S> {
pub(crate) fn new(path: &str, bounds: Vec<(&'static str, Value)>) -> Self {
Self {
path: path.to_string(),
bounds,
extra: Map::new(),
common: Common::default(),
_scope: PhantomData,
}
}
#[must_use]
pub fn format(mut self, format: impl Into<String>) -> Self {
self.extra
.insert("format".to_string(), Value::String(format.into()));
self
}
#[must_use]
pub fn time_zone(mut self, time_zone: impl Into<String>) -> Self {
self.extra
.insert("time_zone".to_string(), Value::String(time_zone.into()));
self
}
#[must_use]
pub fn relation(mut self, relation: RangeRelation) -> Self {
self.extra.insert(
"relation".to_string(),
Value::String(relation.as_str().to_string()),
);
self
}
common_opts!(common);
}
impl<S> AsQuery<S> for RangeQuery<S> {
fn into_query(self) -> Option<Query<S>> {
let mut body = self.extra;
for (key, value) in self.bounds {
body.insert(key.to_string(), value);
}
self.common.write(&mut body);
Some(single("range", &self.path, Value::Object(body)))
}
}
#[derive(Debug, Clone)]
pub struct Bool<S = Root> {
path: String,
_scope: PhantomData<fn() -> S>,
}
impl<S> Bool<S> {
pub fn at(path: impl Into<String>) -> Self {
Self {
path: path.into(),
_scope: PhantomData,
}
}
pub fn eq(&self, value: impl FlussoValue<kind::Bool>) -> EqQuery<S> {
EqQuery::new(&self.path, bool_value(&value))
}
pub fn exists(&self) -> Query<S> {
exists_q(&self.path)
}
}
impl<S: FlussoDocument> Sortable for Bool<S> {
fn asc(&self) -> Sort {
Sort::field::<S>(&self.path, SortOrder::Asc)
}
fn desc(&self) -> Sort {
Sort::field::<S>(&self.path, SortOrder::Desc)
}
}
#[derive(Debug, Clone)]
pub struct Number<K, S = Root> {
path: String,
_marker: PhantomData<fn() -> (K, S)>,
}
impl<K, S> Number<K, S> {
pub fn at(path: impl Into<String>) -> Self {
Self {
path: path.into(),
_marker: PhantomData,
}
}
pub fn eq(&self, value: impl FlussoValue<K>) -> EqQuery<S> {
EqQuery::new(&self.path, number_value(&value))
}
pub fn any_of(&self, values: impl IntoIterator<Item = impl FlussoValue<K>>) -> TermsQuery<S> {
let array = values.into_iter().map(|v| number_value(&v)).collect();
TermsQuery::new(&self.path, array)
}
pub fn lt(&self, value: impl FlussoValue<K>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("lt", number_value(&value))])
}
pub fn lte(&self, value: impl FlussoValue<K>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("lte", number_value(&value))])
}
pub fn gt(&self, value: impl FlussoValue<K>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("gt", number_value(&value))])
}
pub fn gte(&self, value: impl FlussoValue<K>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("gte", number_value(&value))])
}
pub fn between(&self, low: impl FlussoValue<K>, high: impl FlussoValue<K>) -> RangeQuery<S> {
RangeQuery::new(
&self.path,
vec![("gte", number_value(&low)), ("lte", number_value(&high))],
)
}
pub fn exists(&self) -> Query<S> {
exists_q(&self.path)
}
}
impl<K, S: FlussoDocument> Sortable for Number<K, S> {
fn asc(&self) -> Sort {
Sort::field::<S>(&self.path, SortOrder::Asc)
}
fn desc(&self) -> Sort {
Sort::field::<S>(&self.path, SortOrder::Desc)
}
}
#[derive(Debug, Clone)]
pub struct Date<S = Root> {
path: String,
_scope: PhantomData<fn() -> S>,
}
impl<S> Date<S> {
pub fn at(path: impl Into<String>) -> Self {
Self {
path: path.into(),
_scope: PhantomData,
}
}
pub fn eq(&self, value: impl FlussoValue<kind::Date>) -> EqQuery<S> {
EqQuery::new(&self.path, date_value(&value))
}
pub fn any_of(
&self,
values: impl IntoIterator<Item = impl FlussoValue<kind::Date>>,
) -> TermsQuery<S> {
let array = values.into_iter().map(|v| date_value(&v)).collect();
TermsQuery::new(&self.path, array)
}
pub fn lt(&self, value: impl FlussoValue<kind::Date>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("lt", date_value(&value))])
}
pub fn lte(&self, value: impl FlussoValue<kind::Date>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("lte", date_value(&value))])
}
pub fn gt(&self, value: impl FlussoValue<kind::Date>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("gt", date_value(&value))])
}
pub fn gte(&self, value: impl FlussoValue<kind::Date>) -> RangeQuery<S> {
RangeQuery::new(&self.path, vec![("gte", date_value(&value))])
}
pub fn between(
&self,
low: impl FlussoValue<kind::Date>,
high: impl FlussoValue<kind::Date>,
) -> RangeQuery<S> {
RangeQuery::new(
&self.path,
vec![("gte", date_value(&low)), ("lte", date_value(&high))],
)
}
pub fn exists(&self) -> Query<S> {
exists_q(&self.path)
}
}
impl<S: FlussoDocument> Sortable for Date<S> {
fn asc(&self) -> Sort {
Sort::field::<S>(&self.path, SortOrder::Asc)
}
fn desc(&self) -> Sort {
Sort::field::<S>(&self.path, SortOrder::Desc)
}
}