From c5d4f5fa282fd2671cc80c2bc8cef099171932f7 Mon Sep 17 00:00:00 2001 From: glendc Date: Thu, 18 Apr 2024 14:36:04 +0200 Subject: [PATCH] add bugfixes and breaking changes for v0.3 - closes #5: bugfix for any filter maps: rows with an any value can now also match otherwise unknown variants of this filter map type - closes #6: QoL improvement that can break some users, where the db queries now accept for filter maps 'impl Into' instead of 'T', making it especially for newtype-like filter map types easier to use --- CHANGELOG.md | 17 ++++++++++ Cargo.toml | 4 +-- README.md | 2 +- benches/proxies/mod.rs | 6 ++-- venndb-macros/Cargo.toml | 2 +- venndb-macros/src/generate_db.rs | 44 ++++++++++++++------------ venndb-usage/src/main.rs | 53 ++++++++++++++++++++++++++++++++ 7 files changed, 102 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2502de..719e238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# 0.3.0 (2024-04-18) + +Breaking changes: + +* [[`#6`](https://github.com/plabayo/venndb/issues/6)] query filter maps now accept arguments as `impl Into` instead of `T`, + this can be a breaking change for users that were inserting them as `value.into()`, + as the compiler will for these cases now give a compile time error due to the now introduced ambiguity; + * Migration is as easy as removing the manual `.into()` (like) calls that you previously had to add yourself; + +Bug Fixes from [0.2.1](#021-2024-04-15): + +* [[`#5`](https://github.com/plabayo/venndb/issues/5)] any filters now also allow rows to match on unknown filter map variants. + * e.g. if you have a `MyType` filter map and have not a single row that has `"foo"` as value, + then all rows that that have a value for which `assert!(Any::is_any(value: MyType))` will still work. + * prior to this bug fix these values could not be matched on, and the `any` rows would only hit + if there were also rows that had that value explicitly defined. + # 0.2.1 (2024-04-15) A backwards compatible patch for [v0.2.0](#020-2024-04-15), diff --git a/Cargo.toml b/Cargo.toml index 80afe18..3852b2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/plabayo/venndb" keywords = ["database", "db", "memory", "bits"] categories = ["database"] authors = ["Glen De Cauwsemaecker "] -version = "0.2.1" +version = "0.3.0" rust-version = "1.75.0" [package.metadata.docs.rs] @@ -23,7 +23,7 @@ rustdoc-args = ["--cfg", "docsrs"] bitvec = "1.0.1" hashbrown = "0.14.3" rand = "0.8.5" -venndb-macros = { version = "0.2.1", path = "venndb-macros" } +venndb-macros = { version = "0.3.0", path = "venndb-macros" } [dev-dependencies] divan = "0.1.14" diff --git a/README.md b/README.md index 3f18059..093ad2a 100644 --- a/README.md +++ b/README.md @@ -662,7 +662,7 @@ Query (e.g. `EmployeeInMemDBQuery`) | `EmployeeInMemDBQuery::reset(&mut self) -> &mut Self` | reset the query, bringing it back to the clean state it has on creation | | `EmployeeInMemDBQuery::execute(&self) -> Option>` | return the result of the query using the set filters. It will be `None` in case no rows matched the defined filters. Or put otherwise, the result will contain at least one row when `Some(_)` is returned. | | `EmployeeInMemDBQuery::is_manager(&mut self, value: bool) -> &mut Self` | a filter setter for a `bool` filter. One such method per `bool` filter (that isn't `skip`ped) will be available. E.g. if you have ` foo` filter then there will be a `EmployeeInMemDBQuery:foo` method. For _bool_ filters that are optional (`Option`) this method is also generated just the same. | -| `EmployeeInMemDBQuery::department(&mut self, value: Department) -> &mut Self` | a filter (map) setter for a non-`bool` filter. One such method per non-`bool` filter will be available. You can also `skip` these, but that's of course a bit pointless. The type will be equal to the actual field type. And the name will once again be equal to the original field name. Filter maps that have a `Option` type have exactly the same signature. | +| `EmployeeInMemDBQuery::department(&mut self, value: impl ::std::convert::Into) -> &mut Self` | a filter (map) setter for a non-`bool` filter. One such method per non-`bool` filter will be available. You can also `skip` these, but that's of course a bit pointless. The type will be equal to the actual field type. And the name will once again be equal to the original field name. Filter maps that have a `Option` type have exactly the same signature. | Query Result (e.g. `EmployeeInMemDBQueryResult`) diff --git a/benches/proxies/mod.rs b/benches/proxies/mod.rs index a95ab21..fd139e2 100644 --- a/benches/proxies/mod.rs +++ b/benches/proxies/mod.rs @@ -94,7 +94,7 @@ impl ProxyDB for InMemProxyDB { fn any_tcp(&self, pool: &str, country: &str) -> Option> { let mut query = self.query(); - query.tcp(true).pool(pool.into()).country(country.into()); + query.tcp(true).pool(pool).country(country); query.execute().map(|result| { let proxy_ref = result.any(); Cow::Borrowed(proxy_ref) @@ -107,8 +107,8 @@ impl ProxyDB for InMemProxyDB { .socks5(true) .datacenter(true) .residential(true) - .pool(pool.into()) - .country(country.into()); + .pool(pool) + .country(country); query.execute().map(|result| { let proxy_ref = result.any(); Cow::Borrowed(proxy_ref) diff --git a/venndb-macros/Cargo.toml b/venndb-macros/Cargo.toml index 90bd81a..79b766f 100644 --- a/venndb-macros/Cargo.toml +++ b/venndb-macros/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/plabayo/venndb" keywords = ["database", "db", "memory", "bits"] categories = ["database", "db"] authors = ["Glen De Cauwsemaecker "] -version = "0.2.1" +version = "0.3.0" rust-version = "1.75.0" [package.metadata.docs.rs] diff --git a/venndb-macros/src/generate_db.rs b/venndb-macros/src/generate_db.rs index c243c05..7ee35e4 100644 --- a/venndb-macros/src/generate_db.rs +++ b/venndb-macros/src/generate_db.rs @@ -397,8 +397,14 @@ fn generate_db_struct_method_append( }; let filter_any_register = match field.filter_any_name() { - Some(name) => quote! { - self.#name.push(::venndb::Any::is_any(&value)); + Some(any_vec) => if field.optional { + quote! { + self.#any_vec.push(data.#name.as_ref().map(::venndb::Any::is_any).unwrap_or_default()); + } + } else { + quote! { + self.#any_vec.push(::venndb::Any::is_any(&data.#name)); + } }, None => quote! {}, }; @@ -441,9 +447,9 @@ fn generate_db_struct_method_append( if field.optional { quote! { + #filter_any_register let #filter_index = match data.#name.clone() { Some(value) => { - #filter_any_register Some(match self.#filter_map_name.entry(value) { ::venndb::__internal::hash_map::Entry::Occupied(entry) => *entry.get(), ::venndb::__internal::hash_map::Entry::Vacant(entry) => { @@ -462,9 +468,8 @@ fn generate_db_struct_method_append( } } else { quote! { - let value = data.#name.clone(); #filter_any_register - let #filter_index = match self.#filter_map_name.entry(value) { + let #filter_index = match self.#filter_map_name.entry(data.#name.clone()) { ::venndb::__internal::hash_map::Entry::Occupied(entry) => *entry.get(), ::venndb::__internal::hash_map::Entry::Vacant(entry) => { let vec_index = self.#filter_vec_name.len(); @@ -700,8 +705,8 @@ fn generate_query_struct_impl( ); Some(quote! { #[doc=#doc] - #vis fn #name(&mut self, value: #ty) -> &mut Self { - self.#name = Some(value); + #vis fn #name(&mut self, value: impl::std::convert::Into<#ty>) -> &mut Self { + self.#name = Some(value.into()); self } }) @@ -752,20 +757,21 @@ fn generate_query_struct_impl( let name = field.name(); let filter_map_name: Ident = field.filter_map_name(); let filter_vec_name: Ident = field.filter_vec_name(); - let value_filter = quote! { - match self.db.#filter_map_name.get(value) { - Some(index) => filter &= &self.db.#filter_vec_name[*index], - None => return None, - }; - }; - let value_filter = if field.any { - quote! { + let value_filter = match field.filter_any_name() { + Some(filter_any_vec) => quote! { if !::venndb::Any::is_any(&value) { - #value_filter + match self.db.#filter_map_name.get(value) { + Some(index) => filter &= &self.db.#filter_vec_name[*index], + None => filter &= &self.db.#filter_any_vec, + }; } - } - } else { - value_filter + }, + None => quote! { + match self.db.#filter_map_name.get(value) { + Some(index) => filter &= &self.db.#filter_vec_name[*index], + None => return None, + }; + }, }; Some(quote! { // Filter by the filterm ap below, only if it is defined as Some(_). diff --git a/venndb-usage/src/main.rs b/venndb-usage/src/main.rs index 46ca2e5..7b0892d 100644 --- a/venndb-usage/src/main.rs +++ b/venndb-usage/src/main.rs @@ -999,3 +999,56 @@ mod tests_v0_2_1 { assert_eq!(results[1].id, 3); } } + +#[cfg(test)] +mod tests_v0_2_2 { + use super::*; + + #[derive(Debug, VennDB)] + pub struct Worker { + #[venndb(key)] + id: u32, + is_admin: bool, + is_active: Option, + #[venndb(filter, any)] + department: Option, + } + + // regression test: + #[test] + fn test_any_row_optional_filter_map_white_rabbit() { + let db = WorkerDB::from_rows(vec![ + Worker { + id: 1, + is_admin: false, + is_active: Some(true), + department: Some(Department::Engineering), + }, + Worker { + id: 2, + is_admin: false, + is_active: None, + department: None, + }, + Worker { + id: 3, + is_admin: false, + is_active: Some(true), + department: Some(Department::Any), + }, + Worker { + id: 4, + is_admin: false, + is_active: Some(true), + department: Some(Department::HR), + }, + ]) + .unwrap(); + + let mut query = db.query(); + query.department(Department::Marketing); + let results = query.execute().unwrap().iter().collect::>(); + assert_eq!(results.len(), 1); + assert_eq!(results[0].id, 3); + } +}