|
@@ -1,22 +1,25 @@
|
|
|
//! Static in-place query construction.
|
|
|
//!
|
|
|
-//! Potential interfaces:
|
|
|
-//! - `microrm::select().by(KVStore::Key, &key).result(qi)`
|
|
|
-//! - `qi.select().by(KVStore::Key, &key).result()`
|
|
|
+//! Interface:
|
|
|
+//! - `qi.get().by(KVStore::Key, &key).result()`
|
|
|
|
|
|
use crate::{entity::EntityColumn, model::Modelable, Entity, Error};
|
|
|
-use std::{marker::PhantomData, collections::HashMap, hash::{Hasher, Hash}};
|
|
|
+use std::{
|
|
|
+ collections::HashMap,
|
|
|
+ hash::{Hash, Hasher},
|
|
|
+ marker::PhantomData,
|
|
|
+};
|
|
|
|
|
|
-use super::{WithID, QueryInterface};
|
|
|
+use super::{QueryInterface, WithID};
|
|
|
|
|
|
-#[derive(Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]
|
|
|
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
pub enum QueryPart {
|
|
|
Root,
|
|
|
- Where
|
|
|
+ Where,
|
|
|
}
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
-pub struct DerivedQuery (HashMap<QueryPart, Vec<String>>);
|
|
|
+pub struct DerivedQuery(HashMap<QueryPart, Vec<String>>);
|
|
|
|
|
|
impl DerivedQuery {
|
|
|
fn new() -> Self {
|
|
@@ -33,15 +36,16 @@ impl DerivedQuery {
|
|
|
let where_ = match self.0.remove(&QueryPart::Where) {
|
|
|
None => String::new(),
|
|
|
Some(v) => {
|
|
|
- format!("WHERE {}",
|
|
|
- v.into_iter().reduce(|a, b| format!("{} AND {}", a, b)).unwrap())
|
|
|
+ format!(
|
|
|
+ "WHERE {}",
|
|
|
+ v.into_iter()
|
|
|
+ .reduce(|a, b| format!("{} AND {}", a, b))
|
|
|
+ .unwrap()
|
|
|
+ )
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- format!("{} {}",
|
|
|
- root,
|
|
|
- where_
|
|
|
- )
|
|
|
+ format!("{} {}", root, where_)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -66,26 +70,37 @@ pub trait QueryComponent: StaticVersion {
|
|
|
}
|
|
|
|
|
|
/// Any query that can be completed/executed
|
|
|
-pub trait Resolvable<'r, 'q, T: Entity>: QueryComponent where 'q: 'r {
|
|
|
+pub trait Resolvable<'r, 'q, T: Entity>: QueryComponent
|
|
|
+where
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
fn qi(&self) -> &'r QueryInterface<'q>;
|
|
|
|
|
|
- fn no_result(self) -> Option<()> where Self: Sized {
|
|
|
+ fn exec(self) -> Result<(), crate::Error> where Self: Sized { self.no_result() }
|
|
|
+ fn one(self) -> Result<Option<WithID<T>>, crate::Error> where Self: Sized { self.result() }
|
|
|
+ fn all(self) -> Result<Vec<WithID<T>>, crate::Error> where Self: Sized { self.results() }
|
|
|
+
|
|
|
+ fn no_result(self) -> Result<(), crate::Error>
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
|
self.contribute(&mut hasher);
|
|
|
|
|
|
self.qi().with_cache(
|
|
|
hasher.finish(),
|
|
|
- || {
|
|
|
- self.qi().db.conn.prepare(self.derive().assemble()).unwrap()
|
|
|
- },
|
|
|
+ || self.qi().db.conn.prepare(self.derive().assemble()).unwrap(),
|
|
|
|stmt| {
|
|
|
- self.bind(stmt).ok()?;
|
|
|
+ self.bind(stmt)?;
|
|
|
|
|
|
self.qi().expect_no_result(stmt)
|
|
|
- }
|
|
|
+ },
|
|
|
)
|
|
|
}
|
|
|
- fn result(self) -> Option<WithID<T>> where Self: Sized {
|
|
|
+ fn result(self) -> Result<Option<WithID<T>>, crate::Error>
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
|
self.contribute(&mut hasher);
|
|
|
self.qi().with_cache(
|
|
@@ -95,41 +110,65 @@ pub trait Resolvable<'r, 'q, T: Entity>: QueryComponent where 'q: 'r {
|
|
|
self.qi().db.conn.prepare(query).unwrap()
|
|
|
},
|
|
|
|stmt| {
|
|
|
- self.bind(stmt).ok()?;
|
|
|
+ self.bind(stmt)?;
|
|
|
|
|
|
self.qi().expect_one_result(stmt, &mut |stmt| {
|
|
|
- let id: i64 = stmt.read(0).ok()?;
|
|
|
- Some(WithID::wrap(
|
|
|
- T::build_from(stmt).ok()?,
|
|
|
- id,
|
|
|
- ))
|
|
|
+ let id: i64 = stmt.read(0)?;
|
|
|
+ Ok(WithID::wrap(T::build_from(stmt)?, id))
|
|
|
})
|
|
|
- }
|
|
|
+ },
|
|
|
)
|
|
|
}
|
|
|
- fn results(self) -> Vec<T> where Self: Sized {
|
|
|
+ fn results(self) -> Result<Vec<WithID<T>>, crate::Error>
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
|
self.contribute(&mut hasher);
|
|
|
self.qi().with_cache(
|
|
|
hasher.finish(),
|
|
|
- || {
|
|
|
- self.qi().db.conn.prepare(self.derive().assemble()).unwrap()
|
|
|
- },
|
|
|
+ || self.qi().db.conn.prepare(self.derive().assemble()).unwrap(),
|
|
|
|stmt| {
|
|
|
- self.bind(stmt);
|
|
|
+ self.bind(stmt)?;
|
|
|
|
|
|
- todo!()
|
|
|
- }
|
|
|
+ let mut res = Vec::new();
|
|
|
+ loop {
|
|
|
+ let state = stmt.next()?;
|
|
|
+ if state == sqlite::State::Done {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ let id: i64 = stmt.read(0)?;
|
|
|
+ res.push(WithID::wrap(T::build_from(stmt)?, id));
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(res)
|
|
|
+ },
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Any query that can have a WHERE clause attached to it
|
|
|
-pub trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table> where 'q: 'r{
|
|
|
+pub trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table>
|
|
|
+where
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
type Table: Entity;
|
|
|
|
|
|
- fn by<C: EntityColumn<Entity = Self::Table>, G: Modelable + ?Sized>(self, col: C, given: &'r G) -> Filter<'r, 'q, Self, C, G> where Self: Sized {
|
|
|
- Filter { wrap: self, col, given, _ghost: PhantomData }
|
|
|
+ fn by<C: EntityColumn<Entity = Self::Table>, G: Modelable + ?Sized>(
|
|
|
+ self,
|
|
|
+ col: C,
|
|
|
+ given: &'r G,
|
|
|
+ ) -> Filter<'r, 'q, Self, C, G>
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
+ Filter {
|
|
|
+ wrap: self,
|
|
|
+ col,
|
|
|
+ given,
|
|
|
+ _ghost: PhantomData,
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -140,17 +179,23 @@ pub struct Select<'r, 'q, T: Entity> {
|
|
|
|
|
|
impl<'r, 'q, T: Entity> Select<'r, 'q, T> {
|
|
|
pub fn new(qi: &'r QueryInterface<'q>) -> Self {
|
|
|
- Self { qi, _ghost: std::marker::PhantomData }
|
|
|
+ Self {
|
|
|
+ qi,
|
|
|
+ _ghost: std::marker::PhantomData,
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl <'r, 'q, T: Entity> StaticVersion for Select<'r, 'q, T> {
|
|
|
+impl<'r, 'q, T: Entity> StaticVersion for Select<'r, 'q, T> {
|
|
|
type Is = Select<'static, 'static, T>;
|
|
|
}
|
|
|
|
|
|
impl<'r, 'q, T: Entity> QueryComponent for Select<'r, 'q, T> {
|
|
|
fn derive(&self) -> DerivedQuery {
|
|
|
- DerivedQuery::new().add(QueryPart::Root, format!("SELECT * FROM {}", T::table_name()))
|
|
|
+ DerivedQuery::new().add(
|
|
|
+ QueryPart::Root,
|
|
|
+ format!("SELECT * FROM {}", T::table_name()),
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
fn contribute<H: Hasher>(&self, hasher: &mut H) {
|
|
@@ -159,7 +204,9 @@ impl<'r, 'q, T: Entity> QueryComponent for Select<'r, 'q, T> {
|
|
|
}
|
|
|
|
|
|
// next binding point is the first
|
|
|
- fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> { Ok(1) }
|
|
|
+ fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> {
|
|
|
+ Ok(1)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Select<'r, 'q, T> {
|
|
@@ -167,7 +214,9 @@ impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Select<'r, 'q, T> {
|
|
|
}
|
|
|
|
|
|
impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Select<'r, 'q, T> {
|
|
|
- fn qi(&self) -> &'r QueryInterface<'q> { self.qi }
|
|
|
+ fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
+ self.qi
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
pub struct Delete<'r, 'q, T: Entity> {
|
|
@@ -177,11 +226,14 @@ pub struct Delete<'r, 'q, T: Entity> {
|
|
|
|
|
|
impl<'r, 'q, T: Entity> Delete<'r, 'q, T> {
|
|
|
pub fn new(qi: &'r QueryInterface<'q>) -> Self {
|
|
|
- Self { qi, _ghost: std::marker::PhantomData }
|
|
|
+ Self {
|
|
|
+ qi,
|
|
|
+ _ghost: std::marker::PhantomData,
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl <'r, 'q, T: Entity> StaticVersion for Delete<'r, 'q, T> {
|
|
|
+impl<'r, 'q, T: Entity> StaticVersion for Delete<'r, 'q, T> {
|
|
|
type Is = Select<'static, 'static, T>;
|
|
|
}
|
|
|
|
|
@@ -196,7 +248,9 @@ impl<'r, 'q, T: Entity> QueryComponent for Delete<'r, 'q, T> {
|
|
|
}
|
|
|
|
|
|
// next binding point is the first
|
|
|
- fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> { Ok(1) }
|
|
|
+ fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> {
|
|
|
+ Ok(1)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Delete<'r, 'q, T> {
|
|
@@ -204,24 +258,40 @@ impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Delete<'r, 'q, T> {
|
|
|
}
|
|
|
|
|
|
impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Delete<'r, 'q, T> {
|
|
|
- fn qi(&self) -> &'r QueryInterface<'q> { self.qi }
|
|
|
+ fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
+ self.qi
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// A concrete WHERE clause
|
|
|
-pub struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> where 'q: 'r {
|
|
|
+pub struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
+where
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
wrap: F,
|
|
|
col: C,
|
|
|
given: &'r G,
|
|
|
- _ghost: PhantomData<&'q ()>
|
|
|
+ _ghost: PhantomData<&'q ()>,
|
|
|
}
|
|
|
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> StaticVersion for Filter<'r, 'q, F, C, G> where <F as StaticVersion>::Is: Filterable<'static, 'static> {
|
|
|
+impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> StaticVersion
|
|
|
+ for Filter<'r, 'q, F, C, G>
|
|
|
+where
|
|
|
+ <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
+{
|
|
|
type Is = Filter<'static, 'static, <F as StaticVersion>::Is, C, u64>;
|
|
|
}
|
|
|
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> QueryComponent for Filter<'r, 'q, F, C, G> where <F as StaticVersion>::Is: Filterable<'static, 'static>, 'q: 'r {
|
|
|
+impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> QueryComponent
|
|
|
+ for Filter<'r, 'q, F, C, G>
|
|
|
+where
|
|
|
+ <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
fn derive(&self) -> DerivedQuery {
|
|
|
- self.wrap.derive().add(QueryPart::Where, format!("{} = ?", self.col.name()))
|
|
|
+ self.wrap
|
|
|
+ .derive()
|
|
|
+ .add(QueryPart::Where, format!("{} = ?", self.col.name()))
|
|
|
}
|
|
|
|
|
|
fn contribute<H: Hasher>(&self, hasher: &mut H) {
|
|
@@ -239,11 +309,22 @@ impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Quer
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Resolvable<'r, 'q, C::Entity> for Filter<'r, 'q, F, C, G> where <F as StaticVersion>::Is: Filterable<'static, 'static>, 'q: 'r {
|
|
|
- fn qi(&self) -> &'r QueryInterface<'q> { self.wrap.qi() }
|
|
|
+impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
+ Resolvable<'r, 'q, C::Entity> for Filter<'r, 'q, F, C, G>
|
|
|
+where
|
|
|
+ <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
+ self.wrap.qi()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Filterable<'r, 'q> for Filter<'r, 'q, F, C, G> where <F as StaticVersion>::Is: Filterable<'static, 'static>, 'q: 'r {
|
|
|
+impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Filterable<'r, 'q>
|
|
|
+ for Filter<'r, 'q, F, C, G>
|
|
|
+where
|
|
|
+ <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
type Table = C::Entity;
|
|
|
}
|
|
|
-
|