use std::convert::{TryFrom, TryInto};
use bson::{Bson, Document};
use crate::error::Error;
use crate::ext;
pub enum Comparator<T>
where
T: TryInto<ext::bson::Bson>,
T::Error: Into<ext::bson::ser::Error>,
{
Eq(T),
Gt(T),
Gte(T),
In(Vec<T>),
Lt(T),
Lte(T),
Ne(T),
Nin(Vec<T>),
Null,
}
impl<T> TryFrom<Comparator<T>> for Bson
where
T: TryInto<ext::bson::Bson>,
T::Error: Into<ext::bson::ser::Error>,
{
type Error = ext::bson::ser::Error;
fn try_from(value: Comparator<T>) -> Result<Self, Self::Error> {
Ok(match value {
Comparator::Eq(t) => bson!({ "$eq": t.try_into().map_err(|e| e.into())?.0 }),
Comparator::Gt(t) => bson!({ "$gt": t.try_into().map_err(|e| e.into())?.0 }),
Comparator::Gte(t) => bson!({ "$gte": t.try_into().map_err(|e| e.into())?.0 }),
Comparator::In(t) => {
let int = t
.into_iter()
.map(|t| t.try_into())
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.into())?;
bson!({ "$in": Bson::Array(int.into_iter().map(|b| b.0).collect()) })
}
Comparator::Lt(t) => bson!({ "$lt": t.try_into().map_err(|e| e.into())?.0 }),
Comparator::Lte(t) => bson!({ "$lte": t.try_into().map_err(|e| e.into())?.0 }),
Comparator::Ne(t) => bson!({ "$ne": t.try_into().map_err(|e| e.into())?.0 }),
Comparator::Nin(t) => {
let int = t
.into_iter()
.map(|t| t.try_into())
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.into())?;
bson!({ "$nin": Bson::Array(int.into_iter().map(|b| b.0).collect()) })
}
Comparator::Null => Bson::Null,
})
}
}
impl<T> TryFrom<Comparator<T>> for ext::bson::Bson
where
T: TryInto<ext::bson::Bson>,
T::Error: Into<ext::bson::ser::Error>,
{
type Error = ext::bson::ser::Error;
fn try_from(value: Comparator<T>) -> Result<Self, Self::Error> {
Ok(ext::bson::Bson(Bson::try_from(value)?))
}
}
pub trait AsFilter<T: Filter> {
fn filter() -> T;
fn into_filter(self) -> T;
}
pub trait Filter {
fn new() -> Self;
fn into_document(self) -> Result<Document, Error>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ext;
pub struct User {
pub name: String,
}
#[derive(Default)]
pub struct UserFilter {
name: Option<Comparator<String>>,
}
impl Filter for UserFilter {
fn new() -> Self {
Self::default()
}
fn into_document(self) -> crate::Result<Document> {
let mut doc = Document::new();
if let Some(value) = self.name {
doc.insert("name", ext::bson::Bson::try_from(value)?.0);
}
Ok(doc)
}
}
impl AsFilter<UserFilter> for User {
fn filter() -> UserFilter {
UserFilter::default()
}
fn into_filter(self) -> UserFilter {
UserFilter {
name: Some(Comparator::Eq(self.name)),
}
}
}
#[test]
fn user_into_filter() {
let user = User {
name: "foo".to_owned(),
};
let f = user.into_filter();
let name = if let Some(Comparator::Eq(val)) = f.name {
val
} else {
"".to_owned()
};
assert_eq!(name, "foo".to_owned());
}
#[test]
fn filter_into_document() {
let filter = UserFilter {
name: Some(Comparator::Eq("foo".to_owned())),
};
let doc = filter.into_document().unwrap();
assert_eq!(
doc.get("name")
.unwrap()
.as_document()
.unwrap()
.get("$eq")
.unwrap()
.as_str()
.unwrap(),
"foo".to_owned()
);
}
}