Skip to content

Commit

Permalink
Merge pull request #43 from luxbe/master
Browse files Browse the repository at this point in the history
Implement clone derive for trivial_bounds feature
  • Loading branch information
lun3x authored Sep 7, 2023
2 parents 3e1b2fa + 683cc78 commit 07fb4e3
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 22 deletions.
6 changes: 5 additions & 1 deletion multi_index_map/examples/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))]

use crate::inner::MultiIndexOrderMap;
use crate::inner::Order;

Expand All @@ -14,7 +16,9 @@ mod inner {
pub(crate) note: String,
}

// Manually implement Clone, this can be auto generated correctly by rust-analyzer
// Manually implement Clone if trivial_bounds is disabled
// this can be auto generated correctly by rust-analyzer
#[cfg(not(feature = "trivial_bounds"))]
impl Clone for MultiIndexOrderMap {
fn clone(&self) -> Self {
Self {
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/capacity_manipulations.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, Debug)]
Expand Down
38 changes: 38 additions & 0 deletions multi_index_map/tests/clone_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#![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 mut new_map = map.clone();

assert_eq!(new_map.len(), 1);

let elem1 = new_map.remove_by_field1(&TestNonPrimitiveType(42)).unwrap();
assert_eq!(elem1.field1.0, 42);
assert_eq!(elem1.field2, "ElementOne");
assert_eq!(new_map.len(), 0);
}
File renamed without changes.
2 changes: 2 additions & 0 deletions multi_index_map/tests/hashed_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(Hash, PartialEq, Eq, Clone, Debug)]
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/iter_after_modify.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, Debug)]
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/ordered_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(Hash, PartialEq, Eq, Clone, Debug, PartialOrd, Ord)]
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/reverse_iter.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, PartialEq, Debug)]
struct TestElement {
Expand Down
2 changes: 2 additions & 0 deletions multi_index_map/tests/update.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
98 changes: 79 additions & 19 deletions multi_index_map_derive/src/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,30 +69,45 @@ fn index_field_type(
}
}

// 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(
fn generate_type_bounds_table_fields(
fields: &[(Field, FieldIdents, Ordering, Uniqueness)],
type_bounds: ::proc_macro2::TokenStream,
) -> impl Iterator<Item = ::proc_macro2::TokenStream> + '_ {
fields
.iter()
.flat_map(|(f, _idents, ordering, uniqueness)| {
.flat_map(move |(f, _idents, ordering, uniqueness)| {
let ty = &f.ty;

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

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

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

[type_debug, field_debug]
})
}

// 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_debug_type_bounds_table_fields(
fields: &[(Field, FieldIdents, Ordering, Uniqueness)],
) -> impl Iterator<Item = ::proc_macro2::TokenStream> + '_ {
generate_type_bounds_table_fields(fields, quote! {::core::fmt::Debug})
}

// For each indexed field generate a TokenStream of the Clone bound for the field type and the multi_index_map specific type
#[cfg(feature = "trivial_bounds")]
pub(crate) fn generate_clone_type_bounds_table_fields(
fields: &[(Field, FieldIdents, Ordering, Uniqueness)],
) -> impl Iterator<Item = ::proc_macro2::TokenStream> + '_ {
generate_type_bounds_table_fields(fields, quote! {::core::clone::Clone})
}
// 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 @@ -166,6 +181,20 @@ pub(crate) fn generate_lookup_table_debug(
})
}

// For each indexed field generate a TokenStream representing the `.clone()` of that field
#[cfg(feature = "trivial_bounds")]
pub(crate) fn generate_lookup_table_clone(
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! {
#index_name: ::core::clone::Clone::clone(&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 @@ -864,23 +893,52 @@ pub(crate) fn generate_expanded(
#[cfg(feature = "trivial_bounds")] lookup_table_fields_debug: impl Iterator<
Item = proc_macro2::TokenStream,
>,
#[cfg(feature = "trivial_bounds")] lookup_table_field_types: impl Iterator<
#[cfg(feature = "trivial_bounds")] debug_type_bounds_table_fields: impl Iterator<
Item = proc_macro2::TokenStream,
>,
#[cfg(feature = "trivial_bounds")] lookup_table_fields_clone: impl Iterator<
Item = proc_macro2::TokenStream,
>,
#[cfg(feature = "trivial_bounds")] clone_type_bounds_table_fields: 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()
let debug_impl = {
#[cfg(not(feature = "trivial_bounds"))]
quote! {}

#[cfg(feature = "trivial_bounds")]
quote! {
impl ::core::fmt::Debug for #map_name where #element_name: ::core::fmt::Debug,
#(#debug_type_bounds_table_fields)*
{
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()
}
}
}
};

let clone_impl = {
#[cfg(not(feature = "trivial_bounds"))]
quote! {}

#[cfg(feature = "trivial_bounds")]

quote! {
impl ::core::clone::Clone for #map_name where #element_name: ::core::clone::Clone,
#(#clone_type_bounds_table_fields)*
{
#[inline]
fn clone(&self) -> #map_name {
#map_name {
_store: ::core::clone::Clone::clone(&self._store),
#(#lookup_table_fields_clone)*
}
}
}
}
};
Expand All @@ -894,6 +952,8 @@ pub(crate) fn generate_expanded(

#debug_impl

#clone_impl

impl #map_name {
#element_vis fn with_capacity(n: usize) -> #map_name {
#map_name {
Expand Down
16 changes: 14 additions & 2 deletions multi_index_map_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,18 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
#[cfg(feature = "trivial_bounds")]
let lookup_table_fields_debug = generators::generate_lookup_table_debug(&indexed_fields);

#[cfg(feature = "trivial_bounds")]
let lookup_table_fields_clone = generators::generate_lookup_table_clone(&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 debug_type_bounds_table_fields =
generators::generate_debug_type_bounds_table_fields(&indexed_fields);

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

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

Expand Down Expand Up @@ -127,7 +135,11 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
#[cfg(feature = "trivial_bounds")]
lookup_table_fields_debug,
#[cfg(feature = "trivial_bounds")]
lookup_table_field_types,
debug_type_bounds_table_fields,
#[cfg(feature = "trivial_bounds")]
lookup_table_fields_clone,
#[cfg(feature = "trivial_bounds")]
clone_type_bounds_table_fields,
);

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

0 comments on commit 07fb4e3

Please sign in to comment.