Skip to content

Commit

Permalink
Merge pull request #42 from lun3x/trivial_bounds
Browse files Browse the repository at this point in the history
Add trivial_bounds feature to enable Debug impl
  • Loading branch information
lun3x authored Sep 1, 2023
2 parents 348285a + 3d91fae commit 3e1b2fa
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 17 deletions.
3 changes: 3 additions & 0 deletions multi_index_map/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ criterion = "0.5.0"
[[bench]]
name = "performance"
harness = false

[features]
trivial_bounds = ["multi_index_map_derive/trivial_bounds"]
33 changes: 33 additions & 0 deletions multi_index_map/tests/debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))]
#![cfg(feature = "trivial_bounds")]
use multi_index_map::MultiIndexMap;

#[derive(Hash, PartialEq, Eq, Clone, Debug)]
struct TestNonPrimitiveType(u64);

#[derive(MultiIndexMap, Clone, Debug)]
struct TestElement {
#[multi_index(hashed_unique)]
field1: TestNonPrimitiveType,
field2: String,
}

#[test]
fn should_compile() {
let mut map = MultiIndexTestElementMap::default();

// check that formatting produces non empty strings
assert!(!format!("{:?}", map._field1_index).is_empty());
assert!(!format!("{:?}", map._store).is_empty());
assert!(!format!("{:?}", map).is_empty());

let elem1 = TestElement {
field1: TestNonPrimitiveType(42),
field2: "ElementOne".to_string(),
};
map.insert(elem1);

let msg = format!("{:?}", map);
// check if stored field 1 is present in debug output
assert!(msg.contains("42"));
}
2 changes: 2 additions & 0 deletions multi_index_map/tests/get_and_modify_mut.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))]

use multi_index_map::MultiIndexMap;

#[derive(Hash, PartialEq, Eq, Clone)]
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/hashed_unique.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))]

use multi_index_map::MultiIndexMap;

#[derive(Hash, PartialEq, Eq, Clone)]
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/mixed_non_unique.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))]

use multi_index_map::MultiIndexMap;

#[derive(MultiIndexMap)]
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/ordered_unique.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))]

use multi_index_map::MultiIndexMap;

#[derive(Hash, PartialEq, Eq, Clone, PartialOrd, Ord)]
Expand Down
3 changes: 3 additions & 0 deletions multi_index_map_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ convert_case = { version = "0.6" }

[lib]
proc-macro = true

[features]
trivial_bounds = []
109 changes: 92 additions & 17 deletions multi_index_map_derive/src/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,63 @@ pub(crate) fn generate_lookup_tables(
let ty = &f.ty;
let index_name = &idents.index_name;

match uniqueness {
Uniqueness::Unique => match ordering {
Ordering::Hashed => quote! {
#index_name: ::multi_index_map::rustc_hash::FxHashMap<#ty, usize>,
},
Ordering::Ordered => quote! {
#index_name: ::std::collections::BTreeMap<#ty, usize>,
},
},
Uniqueness::NonUnique => match ordering {
Ordering::Hashed => quote! {
#index_name: ::multi_index_map::rustc_hash::FxHashMap<#ty, ::std::collections::BTreeSet<usize>>,
},
Ordering::Ordered => quote! {
#index_name: ::std::collections::BTreeMap<#ty, ::std::collections::BTreeSet<usize>>,
},
},
let field_type = index_field_type(ty, ordering, uniqueness);

quote! {
#index_name: #field_type,
}
})
}

fn index_field_type(
ty: &Type,
ordering: &Ordering,
uniqueness: &Uniqueness,
) -> ::proc_macro2::TokenStream {
match uniqueness {
Uniqueness::Unique => match ordering {
Ordering::Hashed => quote! {
::multi_index_map::rustc_hash::FxHashMap<#ty, usize>
},
Ordering::Ordered => quote! {
::std::collections::BTreeMap<#ty, usize>
},
},
Uniqueness::NonUnique => match ordering {
Ordering::Hashed => quote! {
::multi_index_map::rustc_hash::FxHashMap<#ty, ::std::collections::BTreeSet<usize>>
},
Ordering::Ordered => quote! {
::std::collections::BTreeMap<#ty, ::std::collections::BTreeSet<usize>>
},
},
}
}

// For each indexed field generate a TokenStream of the Debug bound for the field type and the multi_index_map specific type
#[cfg(feature = "trivial_bounds")]
pub(crate) fn generate_lookup_table_field_types(
fields: &[(Field, FieldIdents, Ordering, Uniqueness)],
) -> impl Iterator<Item = ::proc_macro2::TokenStream> + '_ {
fields
.iter()
.flat_map(|(f, _idents, ordering, uniqueness)| {
let ty = &f.ty;

let type_debug = quote! {
#ty: ::core::fmt::Debug,
};

let field_type = index_field_type(ty, ordering, uniqueness);

let field_debug = quote! {
#field_type: ::core::fmt::Debug,
};

[type_debug, field_debug]
})
}

// For each indexed field generate a TokenStream representing initializing the lookup table.
// Used in `with_capacity` initialization
// If lookup table data structures support `with_capacity`, change `default()` and `new()` calls to
Expand Down Expand Up @@ -116,6 +152,20 @@ pub(crate) fn generate_lookup_table_shrink(
})
}

// For each indexed field generate a TokenStream representing a debug struct field
#[cfg(feature = "trivial_bounds")]
pub(crate) fn generate_lookup_table_debug(
fields: &[(Field, FieldIdents, Ordering, Uniqueness)],
) -> impl Iterator<Item = ::proc_macro2::TokenStream> + '_ {
fields.iter().map(|(_f, idents, _ordering, _uniqueness)| {
let index_name = &idents.index_name;

quote! {
.field(stringify!(#index_name), &self.#index_name)
}
})
}

// For each indexed field generate a TokenStream representing inserting the position
// in the backing storage to that field's lookup table
// Unique indexed fields just require a simple insert to the map,
Expand Down Expand Up @@ -811,14 +861,39 @@ pub(crate) fn generate_expanded(
lookup_table_fields_init: impl Iterator<Item = proc_macro2::TokenStream>,
lookup_table_fields_shrink: impl Iterator<Item = proc_macro2::TokenStream>,
lookup_table_fields_reserve: impl Iterator<Item = proc_macro2::TokenStream>,
#[cfg(feature = "trivial_bounds")] lookup_table_fields_debug: impl Iterator<
Item = proc_macro2::TokenStream,
>,
#[cfg(feature = "trivial_bounds")] lookup_table_field_types: impl Iterator<
Item = proc_macro2::TokenStream,
>,
) -> proc_macro2::TokenStream {
#[cfg(not(feature = "trivial_bounds"))]
let debug_impl = quote! {};

#[cfg(feature = "trivial_bounds")]
let debug_impl = quote! {
impl ::core::fmt::Debug for #map_name where #element_name: ::core::fmt::Debug,
#(#lookup_table_field_types)*
{
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct(stringify!(#map_name))
.field("_store", &self._store)
#(#lookup_table_fields_debug)*
.finish()
}
}
};

quote! {
#[derive(Default)]
#element_vis struct #map_name {
_store: ::multi_index_map::slab::Slab<#element_name>,
#(#lookup_table_fields)*
}

#debug_impl

impl #map_name {
#element_vis fn with_capacity(n: usize) -> #map_name {
#map_name {
Expand Down
10 changes: 10 additions & 0 deletions multi_index_map_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,14 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea

let lookup_table_fields_reserve = generators::generate_lookup_table_reserve(&indexed_fields);

#[cfg(feature = "trivial_bounds")]
let lookup_table_fields_debug = generators::generate_lookup_table_debug(&indexed_fields);

let lookup_table_fields_shrink = generators::generate_lookup_table_shrink(&indexed_fields);

#[cfg(feature = "trivial_bounds")]
let lookup_table_field_types = generators::generate_lookup_table_field_types(&indexed_fields);

let inserts = generators::generate_inserts(&indexed_fields);

let removes = generators::generate_removes(&indexed_fields);
Expand Down Expand Up @@ -118,6 +124,10 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
lookup_table_fields_init,
lookup_table_fields_shrink,
lookup_table_fields_reserve,
#[cfg(feature = "trivial_bounds")]
lookup_table_fields_debug,
#[cfg(feature = "trivial_bounds")]
lookup_table_field_types,
);

// Hand the output tokens back to the compiler.
Expand Down

0 comments on commit 3e1b2fa

Please sign in to comment.