Kestrel пре 2 година
родитељ
комит
7a31dbc763

+ 19 - 17
microrm-macros/src/lib.rs

@@ -1,21 +1,23 @@
 use proc_macro::TokenStream;
+use quote::{format_ident, quote};
 use syn::{parse_macro_input, DeriveInput};
-use quote::{quote,format_ident};
 
 use convert_case::{Case, Casing};
 
 fn parse_microrm_ref(attrs: &Vec<syn::Attribute>) -> proc_macro2::TokenStream {
     for attr in attrs {
-        if attr.path.segments.len() == 0 { continue }
+        if attr.path.segments.len() == 0 {
+            continue;
+        }
 
         if attr.tokens.is_empty() {
             if attr.path.segments.last().unwrap().ident == "microrm_internal" {
-                return quote!{ crate }.into()
+                return quote! { crate }.into();
             }
         }
     }
-    
-    quote!{ ::microrm }
+
+    quote! { ::microrm }
 }
 
 /// Turns a serializable/deserializable struct into a microrm entity model.
@@ -34,7 +36,7 @@ fn parse_microrm_ref(attrs: &Vec<syn::Attribute>) -> proc_macro2::TokenStream {
 /// outside the microrm library)
 /// The following are understood on individual fields
 /// - `#[microrm_foreign]`: this is a foreign key (and must be an type implementing `EntityID`)
-#[proc_macro_derive(Entity, attributes(microrm_internal,microrm_foreign))]
+#[proc_macro_derive(Entity, attributes(microrm_internal, microrm_foreign))]
 pub fn derive_entity(tokens: TokenStream) -> TokenStream {
     let input = parse_macro_input!(tokens as DeriveInput);
 
@@ -48,30 +50,29 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
 
     let st = match input.data {
         syn::Data::Struct(st) => st,
-        _ => panic!("Can only use derive(Entity) on structs!")
+        _ => panic!("Can only use derive(Entity) on structs!"),
     };
     let fields = match st.fields {
         syn::Fields::Named(fields) => fields,
-        _ => panic!("Can only use derive(Entity) on non-unit structs with named fields!")
+        _ => panic!("Can only use derive(Entity) on non-unit structs with named fields!"),
     };
 
-    for name in fields.named.iter() {
-        // println!("ty: {:?}", name.ty);
-    }
-
     let mut variants = syn::punctuated::Punctuated::<syn::Ident, syn::token::Comma>::new();
-    let mut field_names = syn::punctuated::Punctuated::<proc_macro2::TokenStream, syn::token::Comma>::new();
-    let mut value_references = syn::punctuated::Punctuated::<proc_macro2::TokenStream, syn::token::Comma>::new();
+    let mut field_names =
+        syn::punctuated::Punctuated::<proc_macro2::TokenStream, syn::token::Comma>::new();
+    let mut value_references =
+        syn::punctuated::Punctuated::<proc_macro2::TokenStream, syn::token::Comma>::new();
     for name in fields.named.iter() {
-        let converted_case = format!("{}", name.ident.as_ref().unwrap().clone()).to_case(Case::UpperCamel);
+        let converted_case =
+            format!("{}", name.ident.as_ref().unwrap().clone()).to_case(Case::UpperCamel);
         let converted_case = format_ident!("{}", converted_case);
         variants.push(converted_case.clone());
 
         let field_name = name.ident.as_ref().unwrap().clone();
         let field_name_str = format!("{}", field_name);
-        field_names.push(quote!{ Self::Column::#converted_case => #field_name_str }.into());
+        field_names.push(quote! { Self::Column::#converted_case => #field_name_str }.into());
 
-        value_references.push(quote!{ &self. #field_name });
+        value_references.push(quote! { &self. #field_name });
     }
 
     let field_count = fields.named.iter().count();
@@ -131,6 +132,7 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
     ret
 }
 
+/// Marks a struct as able to be directly used in an Entity to correspond to a single database column.
 #[proc_macro_derive(Modelable, attributes(microrm_internal))]
 pub fn derive_modelable(tokens: TokenStream) -> TokenStream {
     let input = parse_macro_input!(tokens as DeriveInput);

+ 21 - 12
microrm/src/lib.rs

@@ -10,7 +10,7 @@
 //! supported though are possible. However, since by design microrm does not
 //! touch database contents for tables not defined in its model, using raw SQL
 //! for any needed dynamic components may be a better choice.
-//! 
+//!
 //! Querying supports a small subset of SQL expressed as type composition.
 //!
 //! A simple example using a SQLite table as an (unindexed) key/value store
@@ -40,11 +40,11 @@
 //! }
 //! ```
 
+mod meta;
 pub mod model;
 pub mod query;
-mod meta;
 
-pub use microrm_macros::{Entity,Modelable};
+pub use microrm_macros::{Entity, Modelable};
 
 // no need to show the re-exports in the documentation
 #[doc(hidden)]
@@ -61,7 +61,7 @@ pub enum DBError {
     DifferentSchema,
     DropFailure,
     CreateFailure,
-    SanityCheckFailure
+    SanityCheckFailure,
 }
 
 impl std::fmt::Display for DBError {
@@ -70,7 +70,7 @@ impl std::fmt::Display for DBError {
     }
 }
 
-impl std::error::Error for DBError { }
+impl std::error::Error for DBError {}
 
 /// SQLite database connection
 pub struct DB {
@@ -80,7 +80,11 @@ pub struct DB {
 }
 
 impl DB {
-    pub fn new(schema: model::SchemaModel, path: &str, allow_recreate: bool) -> Result<Self, DBError> {
+    pub fn new(
+        schema: model::SchemaModel,
+        path: &str,
+        allow_recreate: bool,
+    ) -> Result<Self, DBError> {
         Self::from_connection(
             rusqlite::Connection::open(path).map_err(|_| DBError::ConnectFailure)?,
             schema,
@@ -139,13 +143,12 @@ impl DB {
 
         if hash.is_none() {
             if !allow_recreate {
-                return Err(DBError::NoSchema)
+                return Err(DBError::NoSchema);
             }
             self.create_schema()?;
-        }
-        else if hash.unwrap().value != self.schema_hash {
+        } else if hash.unwrap().value != self.schema_hash {
             if !allow_recreate {
-                return Err(DBError::DifferentSchema)
+                return Err(DBError::DifferentSchema);
             }
             self.create_schema()?;
         }
@@ -156,12 +159,18 @@ impl DB {
     fn create_schema(&self) -> Result<(), DBError> {
         for ds in self.schema.drop() {
             let prepared = self.conn.prepare(ds);
-            prepared.unwrap().execute([]).map_err(|_| DBError::DropFailure)?;
+            prepared
+                .unwrap()
+                .execute([])
+                .map_err(|_| DBError::DropFailure)?;
         }
 
         for cs in self.schema.create() {
             let prepared = self.conn.prepare(cs);
-            prepared.unwrap().execute([]).map_err(|_| DBError::CreateFailure)?;
+            prepared
+                .unwrap()
+                .execute([])
+                .map_err(|_| DBError::CreateFailure)?;
         }
 
         query::add(

+ 0 - 37
microrm/src/meta.rs

@@ -6,40 +6,3 @@ pub struct Metaschema {
     pub key: String,
     pub value: String,
 }
-
-
-/* below this line is a manual expansion of the Entity derivation macro */
-
-/*
-#[derive(Clone,Copy)]
-#[allow(unused)]
-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 ]
-    }
-}
-
-*/

+ 7 - 7
microrm/src/model.rs

@@ -7,7 +7,7 @@ pub enum ModelError {
     DBError(rusqlite::Error),
     LoadError(String),
     EmptyStoreError,
-    CreateError
+    CreateError,
 }
 
 impl From<rusqlite::Error> for ModelError {
@@ -20,7 +20,7 @@ impl From<ModelError> for rusqlite::Error {
     fn from(e: ModelError) -> Self {
         match e {
             ModelError::DBError(e) => e,
-            _ => panic!()
+            _ => panic!(),
         }
     }
 }
@@ -47,8 +47,8 @@ impl std::error::Error for ModelError {}
 
 /// A database entity, aka a struct representing a row in a table
 pub trait Entity: for<'de> serde::Deserialize<'de> + serde::Serialize {
-    type Column : EntityColumns;
-    type ID : EntityID;
+    type Column: EntityColumns;
+    type ID: EntityID;
     fn table_name() -> &'static str;
     fn column_count() -> usize
     where
@@ -64,12 +64,12 @@ pub trait Entity: for<'de> serde::Deserialize<'de> + serde::Serialize {
 
 /// Trait representing the columns of a database entity
 pub trait EntityColumns {
-    type Entity : Entity;
+    type Entity: Entity;
 }
 
 /// Trait for entity IDs in the database
-pub trait EntityID : std::fmt::Debug + Copy + crate::re_export::rusqlite::ToSql {
-    type Entity : Entity;
+pub trait EntityID: std::fmt::Debug + Copy + crate::re_export::rusqlite::ToSql {
+    type Entity: Entity;
     fn from_raw_id(raw: i64) -> Self;
     fn raw_id(&self) -> i64;
 }

+ 14 - 14
microrm/src/model/create.rs

@@ -1,7 +1,7 @@
 use serde::de::Visitor;
 
-use std::rc::Rc;
 use std::cell::Cell;
+use std::rc::Rc;
 
 #[derive(Debug)]
 pub struct CreateDeserializer<'de> {
@@ -16,7 +16,8 @@ pub struct CreateDeserializer<'de> {
 impl<'de> CreateDeserializer<'de> {
     fn integral_type(&mut self) {
         self.column_types.push("integer");
-        self.column_names.push(self.column_name_stack.pop().unwrap());
+        self.column_names
+            .push(self.column_name_stack.pop().unwrap());
     }
 }
 
@@ -71,25 +72,26 @@ impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut CreateDeserializer<'de> {
 
     fn deserialize_string<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Self::Error> {
         self.column_types.push("text");
-        self.column_names.push(self.column_name_stack.pop().unwrap());
+        self.column_names
+            .push(self.column_name_stack.pop().unwrap());
         v.visit_string("".to_owned())
     }
 
     fn deserialize_bytes<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Self::Error> {
         self.column_types.push("blob");
-        self.column_names.push(self.column_name_stack.pop().unwrap());
+        self.column_names
+            .push(self.column_name_stack.pop().unwrap());
         v.visit_bytes(&[])
     }
 
     fn deserialize_byte_buf<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Self::Error> {
         self.column_types.push("blob");
-        self.column_names.push(self.column_name_stack.pop().unwrap());
+        self.column_names
+            .push(self.column_name_stack.pop().unwrap());
         v.visit_bytes(&[])
     }
 
-    fn deserialize_seq<V: Visitor<'de>>(
-        self, v: V) -> Result<V::Value, Self::Error> {
-
+    fn deserialize_seq<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Self::Error> {
         v.visit_seq(self)
     }
 
@@ -104,8 +106,7 @@ impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut CreateDeserializer<'de> {
             let old_elength = elength.get();
             println!("nested deserialize_struct invoked!");
             todo!();
-        }
-        else {
+        } else {
             self.column_name_stack.extend(fields.iter().rev());
             self.expected_length.set(fields.len());
             let ret = v.visit_seq(self);
@@ -116,7 +117,7 @@ impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut CreateDeserializer<'de> {
     fn deserialize_newtype_struct<V: Visitor<'de>>(
         self,
         name: &'static str,
-        v: V
+        v: V,
     ) -> Result<V::Value, Self::Error> {
         let elength = self.expected_length.clone();
         let old_elength = elength.get();
@@ -137,7 +138,7 @@ impl<'de> serde::de::SeqAccess<'de> for CreateDeserializer<'de> {
         seed: T,
     ) -> Result<Option<T::Value>, Self::Error> {
         if self.expected_length.get() == 0 {
-            return Err(Self::Error::CreateError)
+            return Err(Self::Error::CreateError);
         }
 
         self.expected_length.set(self.expected_length.get() - 1);
@@ -147,7 +148,6 @@ impl<'de> serde::de::SeqAccess<'de> for CreateDeserializer<'de> {
 }
 
 pub fn sql_for<T: crate::model::Entity>() -> (String, String) {
-
     let mut elength = Rc::new(Cell::new(0));
 
     let mut cd = CreateDeserializer {
@@ -247,7 +247,7 @@ mod test {
 
     #[derive(serde::Serialize, serde::Deserialize, crate::Modelable)]
     #[microrm_internal]
-    pub struct NonUnit(u8,u8);
+    pub struct NonUnit(u8, u8);
     #[derive(serde::Serialize, serde::Deserialize, crate::Entity)]
     #[microrm_internal]
     pub struct NonUnitNewtype {

+ 2 - 4
microrm/src/model/store.rs

@@ -1,7 +1,5 @@
-use super::{Entity};
+use super::Entity;
 
-pub fn serialize_as_row<T: Entity>(
-    value: &T,
-) -> Vec<&dyn rusqlite::ToSql> {
+pub fn serialize_as_row<T: Entity>(value: &T) -> Vec<&dyn rusqlite::ToSql> {
     value.values()
 }

+ 17 - 17
microrm/src/query.rs

@@ -1,5 +1,5 @@
+use crate::model::{Entity, EntityColumns, EntityID};
 use crate::DB;
-use crate::model::{Entity,EntityID,EntityColumns};
 
 pub mod condition;
 
@@ -10,14 +10,14 @@ pub mod condition;
 #[derive(Debug)]
 pub struct WithID<T: Entity> {
     wrap: T,
-    id: <T as Entity>::ID
+    id: <T as Entity>::ID,
 }
 
 impl<T: Entity> WithID<T> {
     fn wrap(what: T, raw_id: i64) -> Self {
         Self {
             wrap: what,
-            id: <T as Entity>::ID::from_raw_id(raw_id)
+            id: <T as Entity>::ID::from_raw_id(raw_id),
         }
     }
 }
@@ -84,8 +84,8 @@ pub fn get_one_by<T: Entity<Column = C>, C: EntityColumns<Entity = T>, V: rusqli
 pub fn get_all_by<T: Entity<Column = C>, C: EntityColumns<Entity = T>, V: rusqlite::ToSql>(
     db: &DB,
     c: C,
-    val: V) -> Option<Vec<WithID<T>>> {
-
+    val: V,
+) -> Option<Vec<WithID<T>>> {
     let table_name = <T as Entity>::table_name();
     let column_name = <T as Entity>::name(c);
 
@@ -94,24 +94,24 @@ pub fn get_all_by<T: Entity<Column = C>, C: EntityColumns<Entity = T>, V: rusqli
         .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()?;
+        .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: Entity>(
-    db: &DB,
-    id: <T as Entity>::ID
-) -> Option<WithID<T>> {
+pub fn get_one_by_id<T: Entity>(db: &DB, id: <T as Entity>::ID) -> Option<WithID<T>> {
     let table_name = <T as Entity>::table_name();
     let mut prepared = db
         .conn

+ 9 - 13
microrm/src/query/condition.rs

@@ -1,30 +1,26 @@
-
-pub trait Condition {
-
-}
+pub trait Condition {}
 
 struct Comparison<'val, T: 'val + crate::model::Entity> {
     column: <T as crate::model::Entity>::Column,
-    val: &'val dyn rusqlite::ToSql
+    val: &'val dyn rusqlite::ToSql,
 }
 
-impl<T: crate::model::Entity> Condition for Comparison<'_, T> {
-    
-}
+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 {
+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
+    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> {
-    
-}
+impl<L: Condition, R: Condition> Condition for Conjunction<L, R> {}