use amaters_core::{CipherBlob, ColumnRef, Key, Predicate, Query, Update};
pub struct FluentQueryBuilder {
collection: String,
}
impl FluentQueryBuilder {
pub fn new(collection: impl Into<String>) -> Self {
Self {
collection: collection.into(),
}
}
pub fn get(self, key: Key) -> Query {
Query::Get {
collection: self.collection,
key,
}
}
pub fn set(self, key: Key, value: CipherBlob) -> Query {
Query::Set {
collection: self.collection,
key,
value,
}
}
pub fn delete(self, key: Key) -> Query {
Query::Delete {
collection: self.collection,
key,
}
}
pub fn filter(self, predicate: Predicate) -> Query {
Query::Filter {
collection: self.collection,
predicate,
}
}
pub fn update(self, predicate: Predicate, updates: Vec<Update>) -> Query {
Query::Update {
collection: self.collection,
predicate,
updates,
}
}
pub fn range(self, start: Key, end: Key) -> Query {
Query::Range {
collection: self.collection,
start,
end,
}
}
pub fn where_clause(self) -> PredicateBuilder {
PredicateBuilder::new(self.collection)
}
}
pub struct PredicateBuilder {
collection: String,
}
impl PredicateBuilder {
pub fn new(collection: impl Into<String>) -> Self {
Self {
collection: collection.into(),
}
}
pub fn eq(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
FilterBuilder::new(self.collection, Predicate::Eq(column, value))
}
pub fn gt(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
FilterBuilder::new(self.collection, Predicate::Gt(column, value))
}
pub fn lt(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
FilterBuilder::new(self.collection, Predicate::Lt(column, value))
}
pub fn gte(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
FilterBuilder::new(self.collection, Predicate::Gte(column, value))
}
pub fn lte(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
FilterBuilder::new(self.collection, Predicate::Lte(column, value))
}
}
pub struct FilterBuilder {
collection: String,
predicate: Predicate,
}
impl FilterBuilder {
fn new(collection: String, predicate: Predicate) -> Self {
Self {
collection,
predicate,
}
}
pub fn and(mut self, other: Predicate) -> Self {
self.predicate = Predicate::And(Box::new(self.predicate), Box::new(other));
self
}
pub fn or(mut self, other: Predicate) -> Self {
self.predicate = Predicate::Or(Box::new(self.predicate), Box::new(other));
self
}
#[allow(clippy::should_implement_trait)]
pub fn not(mut self) -> Self {
self.predicate = Predicate::Not(Box::new(self.predicate));
self
}
pub fn build(self) -> Query {
Query::Filter {
collection: self.collection,
predicate: self.predicate,
}
}
pub fn update(self, updates: Vec<Update>) -> Query {
Query::Update {
collection: self.collection,
predicate: self.predicate,
updates,
}
}
}
pub fn query(collection: impl Into<String>) -> FluentQueryBuilder {
FluentQueryBuilder::new(collection)
}
#[cfg(test)]
mod tests {
use super::*;
use amaters_core::col;
#[test]
fn test_query_builder() {
let q = query("users").get(Key::from_str("user:1"));
match q {
Query::Get { collection, key } => {
assert_eq!(collection, "users");
assert_eq!(key.to_string_lossy(), "user:1");
}
_ => panic!("expected Get query"),
}
}
#[test]
fn test_filter_builder() {
let q = query("users")
.where_clause()
.eq(col("age"), CipherBlob::new(vec![1, 2, 3]))
.build();
match q {
Query::Filter {
collection,
predicate,
} => {
assert_eq!(collection, "users");
assert!(matches!(predicate, Predicate::Eq(_, _)));
}
_ => panic!("expected Filter query"),
}
}
#[test]
fn test_complex_filter() {
let q = query("users")
.where_clause()
.eq(col("status"), CipherBlob::new(vec![1]))
.and(Predicate::Gt(col("age"), CipherBlob::new(vec![18])))
.build();
match q {
Query::Filter {
collection,
predicate,
} => {
assert_eq!(collection, "users");
assert!(matches!(predicate, Predicate::And(_, _)));
}
_ => panic!("expected Filter query"),
}
}
#[test]
fn test_update_builder() {
let updates = vec![Update::Set(col("status"), CipherBlob::new(vec![2]))];
let q = query("users")
.where_clause()
.eq(col("id"), CipherBlob::new(vec![1]))
.update(updates);
match q {
Query::Update {
collection,
predicate,
updates,
} => {
assert_eq!(collection, "users");
assert!(matches!(predicate, Predicate::Eq(_, _)));
assert_eq!(updates.len(), 1);
}
_ => panic!("expected Update query"),
}
}
#[test]
fn test_range_query() {
let q = query("data").range(Key::from_str("a"), Key::from_str("z"));
match q {
Query::Range {
collection,
start,
end,
} => {
assert_eq!(collection, "data");
assert_eq!(start.to_string_lossy(), "a");
assert_eq!(end.to_string_lossy(), "z");
}
_ => panic!("expected Range query"),
}
}
}