Ver código fonte

Add dedicated EntityPart impl for EntityIDs.

Kestrel 6 meses atrás
pai
commit
ac2708239f

+ 19 - 14
microrm-macros/src/entity.rs

@@ -200,6 +200,7 @@ pub fn derive(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
     let entity_name = entity_ident.to_string().to_case(Case::Snake);
 
     let id_ident = format_ident!("{}ID", entity_ident);
+    let id_part_ident = format_ident!("{}IDPart", entity_ident);
 
     quote! {
         #(#part_defs)*
@@ -219,20 +220,6 @@ pub fn derive(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
             fn into_raw(self) -> i64 { self.0 }
         }
 
-        impl ::microrm::schema::entity::EntityPart for #id_ident {
-            type Datum = Self;
-            type Entity = #entity_ident;
-
-            fn unique() -> bool { false }
-            fn part_name() -> &'static str { "id" }
-
-            fn desc() -> Option<&'static str> { None }
-
-            fn get_datum(from: &Self::Entity) -> &Self::Datum {
-                unreachable!()
-            }
-        }
-
         impl ::microrm::schema::datum::Datum for #id_ident {
             fn sql_type() -> &'static str {
                 <i64 as ::microrm::schema::datum::Datum>::sql_type()
@@ -264,6 +251,23 @@ pub fn derive(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
 
         impl ::microrm::schema::datum::ConcreteDatum for #id_ident {}
 
+        #[derive(Clone, Copy, Default)]
+        #vis struct #id_part_ident;
+
+        impl ::microrm::schema::entity::EntityPart for #id_part_ident {
+            type Datum = #id_ident;
+            type Entity = #entity_ident;
+
+            fn unique() -> bool { false }
+            fn part_name() -> &'static str { "id" }
+
+            fn desc() -> Option<&'static str> { None }
+
+            fn get_datum(from: &Self::Entity) -> &Self::Datum {
+                unreachable!()
+            }
+        }
+
         impl ::std::fmt::Debug for #entity_ident {
             fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
                 use ::microrm::schema::datum::Datum;
@@ -277,6 +281,7 @@ pub fn derive(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
             type Parts = #parts_list;
             type Keys = #key_list;
             type ID = #id_ident;
+            type IDPart = #id_part_ident;
 
             fn build(values: <Self::Parts as ::microrm::schema::entity::EntityPartList>::DatumList) -> Self {
                 Self {

+ 17 - 2
microrm/src/query.rs

@@ -509,7 +509,22 @@ pub trait Queryable: Clone {
         components::WithComponent::new(self, part, value)
     }
 
-    /// Ask to return at most a single result
+    /// Filter exactly on an entity ID.
+    fn with_id(
+        self,
+        id: <Self::EntityOutput as Entity>::ID,
+    ) -> impl Queryable<
+        EntityOutput = Self::EntityOutput,
+        OutputContainer = Option<Stored<Self::EntityOutput>>,
+    >
+    where
+        Self: Sized,
+    {
+        self.with(<Self::EntityOutput as Entity>::IDPart::default(), id)
+            .first()
+    }
+
+    /// Ask to return at most a single result.
     fn first(
         self,
     ) -> impl Queryable<
@@ -525,7 +540,7 @@ pub trait Queryable: Clone {
     // ----------------------------------------------------------------------
     // Relation-following and joining methods
     // ----------------------------------------------------------------------
-    /// Join based on an existing relation
+    /// Join based on an existing relation.
     fn join<
         AD: RelationInterface + Datum,
         EP: EntityPart<Entity = Self::EntityOutput, Datum = AD>,

+ 15 - 9
microrm/src/schema.rs

@@ -215,20 +215,26 @@ impl<T: 'static + serde::Serialize + serde::de::DeserializeOwned + std::fmt::Deb
 }
 
 /// Helper trait to make working with [`Serialized`] fields a little bit nicer.
-pub trait Serializable: serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug + Clone {
+pub trait Serializable:
+    serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug + Clone
+{
     /// Wrap an eligible object into a [`Serialized`] version of itself.
-    fn into_serialized(self) -> Serialized<Self> where Self: Sized;
+    fn into_serialized(self) -> Serialized<Self>
+    where
+        Self: Sized;
 }
 
-impl<T: 'static + serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug + Clone> Serializable for T {
-    fn into_serialized(self) -> Serialized<Self> where Self: Sized {
-        Serialized {
-            wrapped: self
-        }
+impl<T: 'static + serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug + Clone>
+    Serializable for T
+{
+    fn into_serialized(self) -> Serialized<Self>
+    where
+        Self: Sized,
+    {
+        Serialized { wrapped: self }
     }
 }
 
-
 // ----------------------------------------------------------------------
 // Database specification types
 // ----------------------------------------------------------------------
@@ -263,7 +269,7 @@ impl<T: Entity> IDMap<T> {
 
     /// Look up an Entity in this map by ID.
     pub fn by_id(&self, id: T::ID) -> DBResult<Option<Stored<T>>> {
-        self.with(id, &id).first().get()
+        self.with(T::IDPart::default(), &id).first().get()
     }
 }
 

+ 2 - 5
microrm/src/schema/build.rs

@@ -283,7 +283,7 @@ pub(crate) fn collect_from_database<DB: Database>() -> DatabaseSchema {
     let mut queries = vec![];
 
     // create sorted table list
-    let mut sorted_tables : Vec<_> = tables.into_iter().collect();
+    let mut sorted_tables: Vec<_> = tables.into_iter().collect();
     sorted_tables.sort_by(|a, b| a.0.cmp(&b.0));
 
     for (table_name, table) in sorted_tables {
@@ -303,8 +303,5 @@ pub(crate) fn collect_from_database<DB: Database>() -> DatabaseSchema {
 
     log::trace!("Schema signature: {signature:x}");
 
-    DatabaseSchema {
-        signature,
-        queries,
-    }
+    DatabaseSchema { signature, queries }
 }

+ 3 - 1
microrm/src/schema/entity.rs

@@ -105,7 +105,9 @@ pub trait Entity: 'static + std::fmt::Debug {
     /// [`EmptyList`] if no keys are defined.
     type Keys: EntityPartList<Entity = Self>;
     /// The corresponding [`EntityID`] type for this Entity.
-    type ID: EntityID<Entity = Self> + EntityPart<Datum = Self::ID, Entity = Self>;
+    type ID: EntityID<Entity = Self>;
+    /// The [`EntityPart`] type for `Self::ID`.
+    type IDPart: EntityPart<Datum = Self::ID, Entity = Self>;
 
     /// Construct an instance of this entity from a list of datums that matches each part, in
     /// order.

+ 6 - 1
microrm/tests/derive.rs

@@ -112,7 +112,12 @@ fn delete_test() {
         .expect("couldn't insert test person");
     assert!(db.people.by_id(id).expect("couldn't query db").is_some());
 
-    db.people.with(id, &id).delete();
+    db.people
+        .with(
+            <Person as microrm::schema::entity::Entity>::IDPart::default(),
+            &id,
+        )
+        .delete();
 
     assert!(db.people.by_id(id).expect("couldn't query db").is_none());
 }

+ 6 - 2
microrm/tests/manual_construction.rs

@@ -54,8 +54,11 @@ impl EntityID for SimpleEntityID {
     }
 }
 
-impl EntityPart for SimpleEntityID {
-    type Datum = Self;
+#[derive(Clone, Copy, Default)]
+struct SimpleEntityIDPart;
+
+impl EntityPart for SimpleEntityIDPart {
+    type Datum = SimpleEntityID;
     type Entity = SimpleEntity;
 
     fn unique() -> bool {
@@ -97,6 +100,7 @@ impl Entity for SimpleEntity {
     type Parts = SimpleEntityName;
     type Keys = SimpleEntityName;
     type ID = SimpleEntityID;
+    type IDPart = SimpleEntityIDPart;
 
     fn build(name: String) -> Self {
         Self { name }