|
@@ -7,7 +7,7 @@
|
|
|
use crate::{entity::EntityColumn, model::Modelable, Entity, Error};
|
|
|
use std::{marker::PhantomData, collections::HashMap, hash::Hasher};
|
|
|
|
|
|
-use super::QueryInterface;
|
|
|
+use super::{WithID, QueryInterface};
|
|
|
|
|
|
|
|
|
#[derive(Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]
|
|
@@ -16,7 +16,35 @@ pub enum QueryPart {
|
|
|
Where
|
|
|
}
|
|
|
|
|
|
-pub type DerivedQuery = HashMap<QueryPart, Vec<String>>;
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct DerivedQuery (HashMap<QueryPart, Vec<String>>);
|
|
|
+
|
|
|
+impl DerivedQuery {
|
|
|
+ fn new() -> Self {
|
|
|
+ Self { 0: HashMap::new() }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn add(mut self, to: QueryPart, what: String) -> Self {
|
|
|
+ self.0.entry(to).or_insert_with(|| Vec::new()).push(what);
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
+ fn assemble(mut self) -> String {
|
|
|
+ let root = self.0.remove(&QueryPart::Root).unwrap().remove(0);
|
|
|
+ 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!("{} {}",
|
|
|
+ root,
|
|
|
+ where_
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
trait StaticVersion {
|
|
|
type Is: 'static;
|
|
@@ -36,25 +64,53 @@ trait QueryComponent: StaticVersion {
|
|
|
fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error>;
|
|
|
}
|
|
|
|
|
|
-/// Any query that can be completed/executed
|
|
|
-trait Resolvable<'r, 'q, T: Entity>: QueryComponent {
|
|
|
+// helper functions
|
|
|
+
|
|
|
+fn get_stmt<'a, 'l>(qi: &'a QueryInterface<'l>) -> &'a mut sqlite::Statement<'l> {
|
|
|
+ todo!()
|
|
|
+}
|
|
|
|
|
|
+/// Any query that can be completed/executed
|
|
|
+trait Resolvable<'r, 'q, T: Entity>: QueryComponent where 'q: 'r {
|
|
|
fn qi(&self) -> &'r QueryInterface<'q>;
|
|
|
|
|
|
fn no_result(self) where Self: Sized { todo!() }
|
|
|
- fn result(self) -> Option<T> where Self: Sized { todo!() }
|
|
|
+ fn result(self) -> Option<WithID<T>> where Self: Sized {
|
|
|
+ self.qi().with_cache(
|
|
|
+ Self::type_id(),
|
|
|
+ || {
|
|
|
+ self.qi().db.conn.prepare(self.derive().assemble()).unwrap()
|
|
|
+ },
|
|
|
+ |stmt| {
|
|
|
+ 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,
|
|
|
+ ))
|
|
|
+ })
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
fn results(self) -> Vec<T> where Self: Sized {
|
|
|
-
|
|
|
- // std::any::TypeId::of::<T>();
|
|
|
-
|
|
|
- println!("derivation: {:?}", self.derive());
|
|
|
-
|
|
|
- todo!()
|
|
|
+ self.qi().with_cache(
|
|
|
+ Self::type_id(),
|
|
|
+ || {
|
|
|
+ self.qi().db.conn.prepare(self.derive().assemble()).unwrap()
|
|
|
+ },
|
|
|
+ |stmt| {
|
|
|
+ self.bind(stmt);
|
|
|
+
|
|
|
+ todo!()
|
|
|
+ }
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Any query that can have a WHERE clause attached to it
|
|
|
-trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table> {
|
|
|
+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 {
|
|
@@ -62,20 +118,24 @@ trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-struct Select<'r, 'q, T: Entity> {
|
|
|
+pub struct Select<'r, 'q, T: Entity> {
|
|
|
qi: &'r QueryInterface<'q>,
|
|
|
_ghost: PhantomData<T>,
|
|
|
}
|
|
|
|
|
|
+impl<'r, 'q, T: Entity> Select<'r, 'q, T> {
|
|
|
+ pub fn new(qi: &'r QueryInterface<'q>) -> Self {
|
|
|
+ Self { qi, _ghost: std::marker::PhantomData }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
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 {
|
|
|
- HashMap::from([
|
|
|
- (QueryPart::Root, vec![format!("SELECT * FROM {}", T::table_name())])
|
|
|
- ])
|
|
|
+ DerivedQuery::new().add(QueryPart::Root, format!("SELECT * FROM {}", T::table_name()))
|
|
|
}
|
|
|
|
|
|
// next binding point is the first
|
|
@@ -91,7 +151,7 @@ impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Select<'r, 'q, T> {
|
|
|
}
|
|
|
|
|
|
/// A concrete WHERE clause
|
|
|
-struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> {
|
|
|
+struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> where 'q: 'r {
|
|
|
wrap: F,
|
|
|
col: C,
|
|
|
given: &'r G,
|
|
@@ -102,16 +162,9 @@ impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Stat
|
|
|
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> {
|
|
|
+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 {
|
|
|
- let mut parent = self.wrap.derive();
|
|
|
-
|
|
|
- parent.entry(QueryPart::Where).or_insert_with(|| Vec::new()).push(
|
|
|
- format!("{} = ?", self.col.name()));
|
|
|
-
|
|
|
- // println!("derivation: {:?}", self.derive());
|
|
|
-
|
|
|
- parent
|
|
|
+ self.wrap.derive().add(QueryPart::Where, format!("{} = ?", self.col.name()))
|
|
|
}
|
|
|
|
|
|
fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, crate::Error> {
|
|
@@ -123,11 +176,11 @@ 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> {
|
|
|
+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> {
|
|
|
+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;
|
|
|
}
|
|
|
|
|
@@ -145,19 +198,12 @@ mod test_build {
|
|
|
value: String,
|
|
|
}
|
|
|
|
|
|
- fn simple_test() {
|
|
|
+ #[test]
|
|
|
+ fn simple_get() {
|
|
|
use super::*;
|
|
|
let db = crate::DB::new_in_memory(crate::Schema::new().entity::<KVStore>()).unwrap();
|
|
|
let qi = db.query_interface();
|
|
|
|
|
|
- let select = Select::<KVStore> { qi: &qi, _ghost: PhantomData };
|
|
|
- // drop(select);
|
|
|
- let filter = select.by(KVStore::Key, "abc");
|
|
|
- filter.results();
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn simple_get() {
|
|
|
- simple_test()
|
|
|
+ assert!(qi.get().by(KVStore::Key, "abc").result().is_none());
|
|
|
}
|
|
|
}
|