Răsfoiți Sursa

Removed strum dependency, began writing condition support.

Kestrel 2 ani în urmă
părinte
comite
e5bb22382b

+ 12 - 5
microrm-macros/src/lib.rs

@@ -38,17 +38,24 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
         value_references.push(quote!{ &self. #field_name });
     }
 
+    let field_count = fields.named.iter().count();
+
     let ret = quote!{
-        #[derive(Clone,Copy,strum::IntoStaticStr,strum::EnumCount)]
-        enum #enum_name {
+
+        #[derive(Clone,Copy)]
+        pub enum #enum_name {
             #variants
         }
 
-        impl crate::model::Entity for #struct_name {
+        impl ::microrm::model::EntityColumns for #enum_name {
+            type Entity = #struct_name;
+        }
+
+        impl ::microrm::model::Entity for #struct_name {
             fn table_name() -> &'static str { #table_name }
             type Column = #enum_name;
             fn column_count() -> usize {
-                <Self::Column as strum::EnumCount>::COUNT
+                #field_count
             }
             fn index(c: Self::Column) -> usize {
                 c as usize
@@ -58,7 +65,7 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
                     #field_names
                 }
             }
-            fn values(&self) -> Vec<&dyn rusqlite::ToSql> {
+            fn values(&self) -> Vec<&dyn ::microrm::re_export::rusqlite::ToSql> {
                 vec![ #value_references ]
             }
         }

+ 0 - 2
microrm/Cargo.toml

@@ -11,6 +11,4 @@ sha2 = "0.10"
 rusqlite = "0.27"
 serde = { version = "1.0", features = ["derive"] }
 
-strum = { version = "0.24", features = ["derive"] }
-
 microrm-macros = { path = "../microrm-macros" }

+ 7 - 8
microrm/src/lib.rs

@@ -1,12 +1,11 @@
 pub mod model;
 pub mod query;
+mod meta;
 
 pub use microrm_macros::Entity;
 
-#[derive(Debug, serde::Serialize, serde::Deserialize, Entity)]
-struct Metaschema {
-    key: String,
-    value: String,
+pub mod re_export {
+    pub use rusqlite;
 }
 
 pub struct DB {
@@ -42,7 +41,7 @@ impl DB {
         let ret = Self {
             conn,
             schema_hash: sig,
-            schema: schema.add::<Metaschema>(),
+            schema: schema.add::<meta::Metaschema>(),
         };
         ret.check_schema(allow_recreate);
         ret
@@ -67,7 +66,7 @@ impl DB {
     }
 
     fn check_schema(&self, allow_recreate: bool) {
-        let hash = query::get_one_by::<Metaschema, _>(self, MetaschemaColumns::Key, "schema_hash");
+        let hash = query::get_one_by::<meta::Metaschema, _>(self, meta::MetaschemaColumns::Key, "schema_hash");
 
         if hash.is_none() || hash.unwrap().value != self.schema_hash {
             if !allow_recreate {
@@ -87,7 +86,7 @@ impl DB {
 
             query::add(
                 self,
-                &Metaschema {
+                &meta::Metaschema {
                     key: "schema_hash".to_string(),
                     value: self.schema_hash.clone(),
                 },
@@ -95,7 +94,7 @@ impl DB {
 
             println!(
                 "re-search results: {:?}",
-                query::get_one_by::<Metaschema, _>(self, MetaschemaColumns::Key, "schema_hash")
+                query::get_one_by::<meta::Metaschema, _>(self, meta::MetaschemaColumns::Key, "schema_hash")
             );
         }
     }

+ 38 - 0
microrm/src/meta.rs

@@ -0,0 +1,38 @@
+#[derive(Debug, serde::Serialize, serde::Deserialize)]
+pub struct Metaschema {
+    pub key: String,
+    pub value: String,
+}
+
+/* below this line is a manual expansion of the Entity derivation macro */
+
+#[derive(Clone,Copy)]
+pub enum MetaschemaColumns {
+    Key,
+    Value
+}
+
+impl crate::model::EntityColumns for MetaschemaColumns {
+    type Entity = Metaschema;
+}
+
+impl crate::model::Entity for Metaschema {
+    fn table_name() -> &'static str { "metaschema" }
+    type Column = MetaschemaColumns;
+    fn column_count() -> usize {
+        2
+    }
+    fn index(c: Self::Column) -> usize {
+        c as usize
+    }
+    fn name(c: Self::Column) -> &'static str {
+        match c {
+            Self::Column::Key => "key",
+            Self::Column::Value => "value"
+        }
+    }
+    fn values(&self) -> Vec<&dyn crate::re_export::rusqlite::ToSql> {
+        vec![ &self.key, &self.value ]
+    }
+}
+

+ 22 - 0
microrm/src/model.rs

@@ -15,6 +15,24 @@ impl From<rusqlite::Error> for ModelError {
     }
 }
 
+impl From<ModelError> for rusqlite::Error {
+    fn from(e: ModelError) -> Self {
+        match e {
+            ModelError::DBError(e) => e,
+            _ => panic!()
+        }
+    }
+}
+
+/*impl Into<rusqlite::Error> for ModelError {
+    fn into(self) -> rusqlite::Error {
+        match self {
+            Self::DBError(e) => e,
+            _ => panic!()
+        }
+    }
+}*/
+
 impl std::fmt::Display for ModelError {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
         fmt.write_fmt(format_args!("{:?}", self))
@@ -51,6 +69,10 @@ pub trait Entity: for<'de> serde::Deserialize<'de> + serde::Serialize {
     fn values(&self) -> Vec<&dyn rusqlite::ToSql>;
 }
 
+pub trait EntityColumns {
+    type Entity;
+}
+
 /// How we describe an entire schema
 #[derive(Debug)]
 pub struct SchemaModel {

+ 78 - 8
microrm/src/query.rs

@@ -1,15 +1,23 @@
-pub use crate::DB;
+use crate::DB;
 
-#[derive(Clone, Copy, Debug)]
+pub mod condition;
+
+#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
 pub struct ID(i64);
 
+impl rusqlite::ToSql for ID {
+    fn to_sql(&self) -> Result<rusqlite::types::ToSqlOutput<'_>, rusqlite::Error> {
+        self.0.to_sql()
+    }
+}
+
 #[derive(Debug)]
-pub struct WithID<T: crate::model::Entity + crate::model::Entity> {
+pub struct WithID<T: crate::model::Entity> {
     wrap: T,
     id: ID,
 }
 
-impl<T: crate::model::Entity + crate::model::Entity> WithID<T> {
+impl<T: crate::model::Entity> WithID<T> {
     fn wrap(what: T, raw_id: i64) -> Self {
         Self {
             wrap: what,
@@ -18,26 +26,26 @@ impl<T: crate::model::Entity + crate::model::Entity> WithID<T> {
     }
 }
 
-impl<T: crate::model::Entity + crate::model::Entity> WithID<T> {
+impl<T: crate::model::Entity> WithID<T> {
     pub fn id(&self) -> ID {
         self.id
     }
 }
 
-impl<T: crate::model::Entity + crate::model::Entity> AsRef<T> for WithID<T> {
+impl<T: crate::model::Entity> AsRef<T> for WithID<T> {
     fn as_ref(&self) -> &T {
         &self.wrap
     }
 }
 
-impl<T: crate::model::Entity + crate::model::Entity> std::ops::Deref for WithID<T> {
+impl<T: crate::model::Entity> std::ops::Deref for WithID<T> {
     type Target = T;
     fn deref(&self) -> &Self::Target {
         &self.wrap
     }
 }
 
-impl<T: crate::model::Entity + crate::model::Entity> std::ops::DerefMut for WithID<T> {
+impl<T: crate::model::Entity> std::ops::DerefMut for WithID<T> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.wrap
     }
@@ -70,6 +78,58 @@ pub fn get_one_by<T: crate::model::Entity, V: rusqlite::ToSql>(
     result.ok()
 }
 
+/// Search for all entities matching a property
+pub fn get_all_by<T: crate::model::Entity, V: rusqlite::ToSql>(
+    db: &DB,
+    c: <T as crate::model::Entity>::Column,
+    val: V) -> Option<Vec<WithID<T>>> {
+
+    let table_name = <T as crate::model::Entity>::table_name();
+    let column_name = <T as crate::model::Entity>::name(c);
+    let mut prepared = db
+        .conn
+        .prepare(&format!(
+            "SELECT rowid, tbl.* FROM {} tbl WHERE {} = ?1",
+            table_name, column_name
+        ))
+        .ok()?;
+
+    let rows = prepared.query_map([&val], |row| {
+        let mut deser = crate::model::load::RowDeserializer::from_row(row);
+        Ok(WithID::wrap(
+            T::deserialize(&mut deser)?,
+            row.get(0).expect("can get rowid"),
+        ))
+    }).ok()?;
+
+    Some(rows.map(|x| x.unwrap()).collect())
+}
+
+/// Search for an entity by ID
+pub fn get_one_by_id<T: crate::model::Entity>(
+    db: &DB,
+    id: ID
+) -> Option<WithID<T>> {
+    let table_name = <T as crate::model::Entity>::table_name();
+    let mut prepared = db
+        .conn
+        .prepare(&format!(
+            "SELECT rowid, tbl.* FROM {} tbl WHERE rowid = ?1",
+            table_name
+        ))
+        .ok()?;
+
+    let result = prepared.query_row([&id.0], |row| {
+        let mut deser = crate::model::load::RowDeserializer::from_row(row);
+        Ok(WithID::wrap(
+            T::deserialize(&mut deser).expect("deserialization works"),
+            row.get(0).expect("can get rowid"),
+        ))
+    });
+
+    result.ok()
+}
+
 /// Add an entity to its table
 pub fn add<T: crate::model::Entity + serde::Serialize>(db: &DB, m: &T) -> Option<ID> {
     let row = crate::model::store::serialize_as_row(m);
@@ -108,4 +168,14 @@ impl<'a> Context<'a> {
     ) -> Option<WithID<T>> {
         get_one_by(self.db, c, val)
     }
+
+    pub fn get_one_by_id<
+        T: crate::model::Entity + for<'de> serde::Deserialize<'de>,
+        V: rusqlite::ToSql,
+    >(
+        &self,
+        id: ID,
+    ) -> Option<WithID<T>> {
+        get_one_by_id(self.db, id)
+    }
 }

+ 30 - 0
microrm/src/query/condition.rs

@@ -0,0 +1,30 @@
+
+pub trait Condition {
+
+}
+
+struct Comparison<'val, T: 'val + crate::model::Entity> {
+    column: <T as crate::model::Entity>::Column,
+    val: &'val dyn rusqlite::ToSql
+}
+
+impl<T: crate::model::Entity> Condition for Comparison<'_, T> {
+    
+}
+
+pub fn equal<'val, T: 'val + crate::model::Entity>(column: <T as crate::model::Entity>::Column, val: &'val dyn rusqlite::ToSql) -> impl Condition + 'val {
+    Comparison::<T> { column, val }
+}
+
+struct Conjunction<L: Condition, R: Condition> {
+    left: L,
+    right: R
+}
+
+pub fn both<L: Condition, R: Condition>(left: L, right: R) -> impl Condition {
+    Conjunction { left, right }
+}
+
+impl<L: Condition, R: Condition> Condition for Conjunction<L, R> {
+    
+}