Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add trivial_bounds feature to enable Debug impl #42

Merged
merged 14 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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