From 4f7bc3b5270f18819fad1ed06bf21700f80571aa Mon Sep 17 00:00:00 2001 From: Handy-caT <37216852+Handy-caT@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:31:35 +0300 Subject: [PATCH 1/2] add floats to index --- Cargo.toml | 2 + codegen/src/worktable/generator/index.rs | 5 +- .../src/worktable/generator/queries/type.rs | 51 +++++++++-- codegen/src/worktable/generator/row.rs | 21 ++++- codegen/src/worktable/model/column.rs | 13 +++ examples/src/main.rs | 2 +- src/lib.rs | 2 + src/util/mod.rs | 3 + src/util/ordered_float.rs | 29 ++++++ tests/persistence/sync/mod.rs | 20 ++-- tests/worktable/base.rs | 50 ---------- tests/worktable/float.rs | 91 +++++++++++++++++++ tests/worktable/mod.rs | 1 + 13 files changed, 218 insertions(+), 72 deletions(-) create mode 100644 src/util/mod.rs create mode 100644 src/util/ordered_float.rs create mode 100644 tests/worktable/float.rs diff --git a/Cargo.toml b/Cargo.toml index 65e2c90e..499072a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,5 @@ performance_measurement_codegen = { path = "performance_measurement/codegen", ve performance_measurement = { path = "performance_measurement", version = "0.1.0", optional = true } indexset = { version = "0.11.2", features = ["concurrent", "cdc", "multimap"] } convert_case = "0.6.0" +ordered-float = "5.0.0" +serde = { version = "1.0.215", features = ["derive"] } diff --git a/codegen/src/worktable/generator/index.rs b/codegen/src/worktable/generator/index.rs index 43f7ca04..30e7d394 100644 --- a/codegen/src/worktable/generator/index.rs +++ b/codegen/src/worktable/generator/index.rs @@ -1,6 +1,7 @@ use crate::name_generator::WorktableNameGenerator; use crate::worktable::generator::Generator; +use crate::worktable::generator::queries::r#type::map_to_uppercase; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::quote; @@ -188,7 +189,7 @@ impl Generator { let match_arm = if let Some(t) = self.columns.columns_map.get(&idx.field) { let type_str = t.to_string(); - let variant_ident = Ident::new(&type_str.to_uppercase(), Span::mixed_site()); + let variant_ident = Ident::new(&map_to_uppercase(&type_str), Span::mixed_site()); let (new_value_expr, old_value_expr) = if type_str == "String" { (quote! { new.to_string() }, quote! { old.to_string() }) @@ -317,7 +318,7 @@ impl Generator { let match_arm = if let Some(t) = self.columns.columns_map.get(&idx.field) { let type_str = t.to_string(); - let variant_ident = Ident::new(&type_str.to_uppercase(), Span::mixed_site()); + let variant_ident = Ident::new(&map_to_uppercase(&type_str), Span::mixed_site()); let (new_value_expr, old_value_expr) = if type_str == "String" { (quote! { new.to_string() }, quote! { old.to_string() }) diff --git a/codegen/src/worktable/generator/queries/type.rs b/codegen/src/worktable/generator/queries/type.rs index 2a62e59d..8cc6a9da 100644 --- a/codegen/src/worktable/generator/queries/type.rs +++ b/codegen/src/worktable/generator/queries/type.rs @@ -6,6 +6,20 @@ use quote::quote; use crate::name_generator::WorktableNameGenerator; use crate::worktable::generator::Generator; +pub fn map_to_uppercase(str: &String) -> String { + if str.contains("OrderedFloat") { + let mut split = str.split("<"); + let _ = split.next(); + let inner_type = split + .next() + .expect("OrderedFloat def contains inner type") + .replace(">", ""); + format!("Ordered{}", inner_type.to_uppercase().trim()) + } else { + str.to_uppercase() + } +} + impl Generator { pub fn gen_available_types_def(&mut self) -> syn::Result { let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); @@ -22,9 +36,12 @@ impl Generator { let rows: Vec<_> = unique_types .iter() .map(|s| { - let type_ident = Ident::new(s.to_string().as_str(), Span::mixed_site()); - let type_upper = - Ident::new(s.to_string().to_uppercase().as_str(), Span::mixed_site()); + let type_ident: TokenStream = s + .to_string() + .parse() + .expect("should be valid because parsed from declaration"); + let type_upper = map_to_uppercase(s); + let type_upper = Ident::new(type_upper.as_str(), Span::mixed_site()); Some(quote! { #[from] #type_upper(#type_ident), @@ -34,8 +51,7 @@ impl Generator { if !rows.is_empty() { Ok(quote! { - #[derive(rkyv::Archive, Debug, derive_more::Display, rkyv::Deserialize, Clone, rkyv::Serialize)] - #[derive(From, PartialEq)] + #[derive(Clone, Debug, derive_more::Display, From, PartialEq)] #[non_exhaustive] pub enum #avt_type_ident { #(#rows)* @@ -68,9 +84,28 @@ impl Generator { .get(i) .ok_or(syn::Error::new(i.span(), "Unexpected column name"))?; - Ok::<_, syn::Error>(quote! { - pub #i: #type_, - }) + let def = if type_.to_string().contains("OrderedFloat") { + let inner_type = type_.to_string(); + let mut split = inner_type.split("<"); + let _ = split.next(); + let inner_type = split + .next() + .expect("OrderedFloat def contains inner type") + .to_uppercase() + .replace(">", ""); + let ident = Ident::new( + format!("Ordered{}Def", inner_type.trim()).as_str(), + Span::call_site(), + ); + quote! { + #[rkyv(with = #ident)] + pub #i: #type_, + } + } else { + quote! {pub #i: #type_,} + }; + + Ok::<_, syn::Error>(def) }) .collect::, _>>()?; diff --git a/codegen/src/worktable/generator/row.rs b/codegen/src/worktable/generator/row.rs index f6244ee5..5be4d8db 100644 --- a/codegen/src/worktable/generator/row.rs +++ b/codegen/src/worktable/generator/row.rs @@ -72,7 +72,26 @@ impl Generator { .columns_map .iter() .map(|(name, type_)| { - quote! {pub #name: #type_,} + if type_.to_string().contains("OrderedFloat") { + let inner_type = type_.to_string(); + let mut split = inner_type.split("<"); + let _ = split.next(); + let inner_type = split + .next() + .expect("OrderedFloat def contains inner type") + .to_uppercase() + .replace(">", ""); + let ident = Ident::new( + format!("Ordered{}Def", inner_type.trim()).as_str(), + Span::call_site(), + ); + quote! { + #[rkyv(with = #ident)] + pub #name: #type_, + } + } else { + quote! {pub #name: #type_,} + } }) .collect(); diff --git a/codegen/src/worktable/model/column.rs b/codegen/src/worktable/model/column.rs index e790ad98..6be13812 100644 --- a/codegen/src/worktable/model/column.rs +++ b/codegen/src/worktable/model/column.rs @@ -6,6 +6,14 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::spanned::Spanned; +fn is_float(ident: &Ident) -> bool { + match ident.to_string().as_str() { + "f64" => true, + "f32" => true, + _ => false, + } +} + #[derive(Debug, Clone)] pub struct Columns { pub columns_map: HashMap, @@ -31,6 +39,11 @@ impl Columns { for row in rows { let type_ = &row.type_; + let type_ = if is_float(type_) { + quote! { ordered_float::OrderedFloat<#type_> } + } else { + quote! { #type_ } + }; let type_ = if row.optional { quote! { core::option::Option<#type_> } } else { diff --git a/examples/src/main.rs b/examples/src/main.rs index 21a6115b..b88e1bff 100644 --- a/examples/src/main.rs +++ b/examples/src/main.rs @@ -44,7 +44,7 @@ fn main() { attr2: 345, test: 1, id: 0, - attr_float: 100.0, + attr_float: 100.0.into(), attr_string: "String_attr".to_string(), }; diff --git a/src/lib.rs b/src/lib.rs index 1943f774..7c427346 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ mod row; mod table; pub use data_bucket; mod persistence; +mod util; // mod ty; // mod value; @@ -29,6 +30,7 @@ pub mod prelude { }; pub use crate::primary_key::{PrimaryKeyGenerator, PrimaryKeyGeneratorState, TablePrimaryKey}; pub use crate::table::select::{Order, QueryParams, SelectQueryBuilder, SelectQueryExecutor}; + pub use crate::util::{OrderedF32Def, OrderedF64Def}; pub use crate::{ lock::Lock, Difference, IndexMap, IndexMultiMap, TableIndex, TableIndexCdc, TableRow, TableSecondaryIndex, TableSecondaryIndexCdc, WorkTable, WorkTableError, diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 00000000..a2cbe109 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,3 @@ +mod ordered_float; + +pub use ordered_float::{OrderedF32Def, OrderedF64Def}; diff --git a/src/util/ordered_float.rs b/src/util/ordered_float.rs new file mode 100644 index 00000000..598be510 --- /dev/null +++ b/src/util/ordered_float.rs @@ -0,0 +1,29 @@ +use rkyv::{Archive, Deserialize, Serialize}; + +#[derive(Archive, Serialize, Deserialize)] +#[rkyv(remote = ordered_float::OrderedFloat, archived = ArchivedF64)] +#[rkyv(derive(Debug))] +pub struct OrderedF64Def { + #[rkyv(getter = std::ops::Deref::deref)] + value: f64, +} + +impl From for ordered_float::OrderedFloat { + fn from(value: OrderedF64Def) -> Self { + ordered_float::OrderedFloat(value.value) + } +} + +#[derive(Archive, Serialize, Deserialize)] +#[rkyv(remote = ordered_float::OrderedFloat, archived = ArchivedF32)] +#[rkyv(derive(Debug))] +pub struct OrderedF32Def { + #[rkyv(getter = std::ops::Deref::deref)] + value: f32, +} + +impl From for ordered_float::OrderedFloat { + fn from(value: OrderedF32Def) -> Self { + ordered_float::OrderedFloat(value.value) + } +} diff --git a/tests/persistence/sync/mod.rs b/tests/persistence/sync/mod.rs index 432fa9d7..fb47fb7e 100644 --- a/tests/persistence/sync/mod.rs +++ b/tests/persistence/sync/mod.rs @@ -51,7 +51,7 @@ fn test_space_insert_sync() { let row = TestSyncRow { another: 42, non_unique: 0, - field: 0.234, + field: 0.234.into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -91,7 +91,7 @@ fn test_space_insert_many_sync() { let row = TestSyncRow { another: i, non_unique: (i % 4) as u32, - field: i as f64 / 100.0, + field: (i as f64 / 100.0).into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -135,7 +135,7 @@ fn test_space_update_full_sync() { let row = TestSyncRow { another: 42, non_unique: 0, - field: 0.0, + field: 0.0.into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -143,7 +143,7 @@ fn test_space_update_full_sync() { .update(TestSyncRow { another: 13, non_unique: 0, - field: 0.0, + field: 0.0.into(), id: row.id, }) .await @@ -184,7 +184,7 @@ fn test_space_update_query_pk_sync() { let row = TestSyncRow { another: 42, non_unique: 0, - field: 0.0, + field: 0.0.into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -228,12 +228,12 @@ fn test_space_update_query_unique_sync() { let row = TestSyncRow { another: 42, non_unique: 0, - field: 0.0, + field: 0.0.into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); table - .update_field_by_another(FieldByAnotherQuery { field: 1.0 }, 42) + .update_field_by_another(FieldByAnotherQuery { field: 1.0.into() }, 42) .await .unwrap(); table.wait_for_ops().await; @@ -272,7 +272,7 @@ fn test_space_update_query_non_unique_sync() { let row = TestSyncRow { another: 42, non_unique: 10, - field: 0.0, + field: 0.0.into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -313,7 +313,7 @@ fn test_space_delete_sync() { let row = TestSyncRow { another: 42, non_unique: 0, - field: 0.0, + field: 0.0.into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -353,7 +353,7 @@ fn test_space_delete_query_sync() { let row = TestSyncRow { another: 42, non_unique: 0, - field: 0.0, + field: 0.0.into(), id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); diff --git a/tests/worktable/base.rs b/tests/worktable/base.rs index ccf7c3fe..e4d765a0 100644 --- a/tests/worktable/base.rs +++ b/tests/worktable/base.rs @@ -30,20 +30,6 @@ worktable! ( } ); -worktable! ( - name: TestFloat, - columns: { - id: u64 primary_key autoincrement, - test: i64, - another: f64, - exchange: String - }, - indexes: { - test_idx: test unique, - exchnage_idx: exchange, - } -); - #[test] fn table_name() { let table = TestWorkTable::default(); @@ -659,42 +645,6 @@ fn select_all_where_by_eq_string_test() { assert_eq!(equal.len(), 1); } -#[test] -fn select_all_range_float_test() { - let table = TestFloatWorkTable::default(); - - let row1 = TestFloatRow { - id: table.get_next_pk().into(), - test: 3, - another: 100.0, - exchange: "M".to_string(), - }; - let row2 = TestFloatRow { - id: table.get_next_pk().into(), - test: 1, - another: 200.0, - exchange: "N".to_string(), - }; - let row3 = TestFloatRow { - id: table.get_next_pk().into(), - test: 2, - another: 300.0, - exchange: "P".to_string(), - }; - - let _ = table.insert(row1.clone()).unwrap(); - let _ = table.insert(row2.clone()).unwrap(); - let _ = table.insert(row3.clone()).unwrap(); - - let all = table - .select_all() - .where_by(|row| row.another > 99.0 && row.another < 300.0) - .execute() - .unwrap(); - - assert_eq!(all.len(), 2); -} - #[test] fn select_all_where_by_contains_string_test() { let table = TestWorkTable::default(); diff --git a/tests/worktable/float.rs b/tests/worktable/float.rs new file mode 100644 index 00000000..0f86c85d --- /dev/null +++ b/tests/worktable/float.rs @@ -0,0 +1,91 @@ +use worktable::prelude::*; +use worktable::worktable; + +worktable! ( + name: TestFloat, + columns: { + id: u64 primary_key autoincrement, + test: i64, + another: f64, + exchange: String + }, + indexes: { + test_idx: test unique, + exchnage_idx: exchange, + another_idx: another + } +); + +#[test] +fn select_all_range_float_test() { + let table = TestFloatWorkTable::default(); + + let row1 = TestFloatRow { + id: table.get_next_pk().into(), + test: 3, + another: 100.0.into(), + exchange: "M".to_string(), + }; + let row2 = TestFloatRow { + id: table.get_next_pk().into(), + test: 1, + another: 200.0.into(), + exchange: "N".to_string(), + }; + let row3 = TestFloatRow { + id: table.get_next_pk().into(), + test: 2, + another: 300.0.into(), + exchange: "P".to_string(), + }; + + let _ = table.insert(row1.clone()).unwrap(); + let _ = table.insert(row2.clone()).unwrap(); + let _ = table.insert(row3.clone()).unwrap(); + + let all = table + .select_all() + .where_by(|row| row.another > 99.0.into() && row.another < 300.0.into()) + .execute() + .unwrap(); + + assert_eq!(all.len(), 2); + assert!(all.contains(&row1)); + assert!(all.contains(&row2)) +} + +#[test] +fn select_by_another_test() { + let table = TestFloatWorkTable::default(); + + let row1 = TestFloatRow { + id: table.get_next_pk().into(), + test: 3, + another: 100.0.into(), + exchange: "M".to_string(), + }; + let row2 = TestFloatRow { + id: table.get_next_pk().into(), + test: 1, + another: 100.0.into(), + exchange: "N".to_string(), + }; + let row3 = TestFloatRow { + id: table.get_next_pk().into(), + test: 2, + another: 200.0.into(), + exchange: "P".to_string(), + }; + + let _ = table.insert(row1.clone()).unwrap(); + let _ = table.insert(row2.clone()).unwrap(); + let _ = table.insert(row3.clone()).unwrap(); + + let where_100 = table.select_by_another(100.0.into()).execute().unwrap(); + assert_eq!(where_100.len(), 2); + assert!(where_100.contains(&row1)); + assert!(where_100.contains(&row2)); + let where_200 = table.select_by_another(200.0.into()).execute().unwrap(); + assert_eq!(where_200.len(), 1); + assert!(where_200.contains(&row3)); +} diff --git a/tests/worktable/mod.rs b/tests/worktable/mod.rs index 0f78eb00..448bd4cc 100644 --- a/tests/worktable/mod.rs +++ b/tests/worktable/mod.rs @@ -3,6 +3,7 @@ mod base; mod config; mod count; mod custom_pk; +mod float; mod index; mod option; mod tuple_primary_key; From 243c64107a9efe577e0c77799554907ad5d3be11 Mon Sep 17 00:00:00 2001 From: Handy-caT <37216852+Handy-caT@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:32:58 +0300 Subject: [PATCH 2/2] clippy --- codegen/src/worktable/generator/queries/type.rs | 2 +- codegen/src/worktable/model/column.rs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/codegen/src/worktable/generator/queries/type.rs b/codegen/src/worktable/generator/queries/type.rs index 8cc6a9da..a158e387 100644 --- a/codegen/src/worktable/generator/queries/type.rs +++ b/codegen/src/worktable/generator/queries/type.rs @@ -6,7 +6,7 @@ use quote::quote; use crate::name_generator::WorktableNameGenerator; use crate::worktable::generator::Generator; -pub fn map_to_uppercase(str: &String) -> String { +pub fn map_to_uppercase(str: &str) -> String { if str.contains("OrderedFloat") { let mut split = str.split("<"); let _ = split.next(); diff --git a/codegen/src/worktable/model/column.rs b/codegen/src/worktable/model/column.rs index 6be13812..388cf6bf 100644 --- a/codegen/src/worktable/model/column.rs +++ b/codegen/src/worktable/model/column.rs @@ -7,11 +7,7 @@ use quote::quote; use syn::spanned::Spanned; fn is_float(ident: &Ident) -> bool { - match ident.to_string().as_str() { - "f64" => true, - "f32" => true, - _ => false, - } + matches!(ident.to_string().as_str(), "f64" | "f32") } #[derive(Debug, Clone)]