Skip to content

Commit

Permalink
Merge pull request lun3x#20 from lun3x/per_field_vis
Browse files Browse the repository at this point in the history
Per field visibility
  • Loading branch information
lun3x authored Apr 24, 2023
2 parents 722c397 + a123c1f commit a086ff8
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 74 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "multi_index_map"
version = "0.4.2"
version = "0.5.0"
edition = "2021"
authors = ["Louis Wyborn <louiswyborn@gmail.com>"]
rust-version = "1.62"
Expand Down Expand Up @@ -33,7 +33,7 @@ proc-macro2 = { version = "1.0"}
proc-macro-error = { version = "1.0" }

# Used to convert the field names into UpperCamelCase for the Iterator names, and to snake_case for the module namespace.
convert_case = { version = "0.5" }
convert_case = { version = "0.6" }

[lib]
proc-macro = true
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ The element must implement `Clone`.
## Example

```rust
use crate::multi_index::MultiIndexOrderMap;
use multi_index_map::MultiIndexMap;

#[derive(MultiIndexMap, Clone, Debug)]
Expand Down
7 changes: 7 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
Version 0.5.0 (2024-04-24)
==========================

- Set MultiIndexMap to same visibility as provided Element. Set each field's relevant methods to the visibility of that field. This allows finer-grained control of method visibility/privacy.
- Remove inner `multi_index_<element_name>` module. Previously this was used to avoid polluting the outer namespace with the Iterators for each field, however now users can now control the visibility per-field, so can create their own inner module if necessary to avoid polluting namespace.
- Change `iter_by_` methods. Now they take `&self`, previously they required `&mut self` but this is not necessary.

Version 0.4.2 (2022-09-06)
==========================

Expand Down
27 changes: 15 additions & 12 deletions examples/main.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use crate::multi_index_order::MultiIndexOrderMap;
use multi_index_map::MultiIndexMap;

#[derive(MultiIndexMap, Debug, Clone)]
struct Order {
#[multi_index(hashed_unique)]
order_id: u32,
#[multi_index(ordered_unique)]
timestamp: u64,
#[multi_index(hashed_non_unique)]
trader_name: String,
note: String,
use crate::inner::MultiIndexOrderMap;
use crate::inner::Order;

mod inner {
use multi_index_map::MultiIndexMap;
#[derive(MultiIndexMap, Debug, Clone)]
pub(crate) struct Order {
#[multi_index(hashed_unique)]
pub(crate) order_id: u32,
#[multi_index(ordered_unique)]
pub(crate) timestamp: u64,
#[multi_index(hashed_non_unique)]
pub(crate) trader_name: String,
pub(crate) note: String,
}
}

fn main() {
Expand Down
96 changes: 44 additions & 52 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
// For each indexed field generate a TokenStream representing all the accessors for the underlying storage via that field's lookup table.
let accessors = fields_to_index().map(|f| {
let field_name = f.ident.as_ref().unwrap();
let field_vis = &f.vis;
let index_name = format_ident!("_{}_index", field_name);
let getter_name = format_ident!("get_by_{}", field_name);
let mut_getter_name = format_ident!("get_mut_by_{}", field_name);
Expand All @@ -183,12 +184,12 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
// in order to return a Vec of references to the backing storage.
let getter = match uniqueness {
Uniqueness::Unique => quote! {
pub(super) fn #getter_name(&self, key: &#ty) -> Option<&#element_name> {
#field_vis fn #getter_name(&self, key: &#ty) -> Option<&#element_name> {
Some(&self._store[*self.#index_name.get(key)?])
}
},
Uniqueness::NonUnique => quote! {
pub(super) fn #getter_name(&self, key: &#ty) -> Vec<&#element_name> {
#field_vis fn #getter_name(&self, key: &#ty) -> Vec<&#element_name> {
if let Some(idxs) = self.#index_name.get(key) {
let mut elem_refs = Vec::with_capacity(idxs.len());
for idx in idxs {
Expand All @@ -210,7 +211,7 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
// SAFETY:
// It is safe to mutate the non-indexed fields, however mutating any of the indexed fields will break the internal invariants.
// If the indexed fields need to be changed, the modify() method must be used.
pub(super) unsafe fn #mut_getter_name(&mut self, key: &#ty) -> Option<&mut #element_name> {
#field_vis unsafe fn #mut_getter_name(&mut self, key: &#ty) -> Option<&mut #element_name> {
Some(&mut self._store[*self.#index_name.get(key)?])
}
},
Expand All @@ -222,15 +223,15 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
// in order to return a Vec elements from the backing storage.
let remover = match uniqueness {
Uniqueness::Unique => quote! {
pub(super) fn #remover_name(&mut self, key: &#ty) -> Option<#element_name> {
#field_vis fn #remover_name(&mut self, key: &#ty) -> Option<#element_name> {
let idx = self.#index_name.remove(key)?;
let elem_orig = self._store.remove(idx);
#(#removes)*
Some(elem_orig)
}
},
Uniqueness::NonUnique => quote! {
pub(super) fn #remover_name(&mut self, key: &#ty) -> Vec<#element_name> {
#field_vis fn #remover_name(&mut self, key: &#ty) -> Vec<#element_name> {
if let Some(idxs) = self.#index_name.remove(key) {
let mut elems = Vec::with_capacity(idxs.len());
for idx in idxs {
Expand All @@ -250,7 +251,7 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
// Unavailable for NonUnique fields for now, because the modification logic gets quite complicated.
let modifier = match uniqueness {
Uniqueness::Unique => quote! {
pub(super) fn #modifier_name(&mut self, key: &#ty, f: impl FnOnce(&mut #element_name)) -> Option<&#element_name> {
#field_vis fn #modifier_name(&mut self, key: &#ty, f: impl FnOnce(&mut #element_name)) -> Option<&#element_name> {
let idx = *self.#index_name.get(key)?;
let elem = &mut self._store[idx];
let elem_orig = elem.clone();
Expand All @@ -274,7 +275,7 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea

#modifier

pub(super) fn #iter_getter_name(&self) -> #iter_name {
#field_vis fn #iter_getter_name(&self) -> #iter_name {
#iter_name {
_store_ref: &self._store,
_iter: self.#index_name.iter(),
Expand All @@ -288,6 +289,7 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
// such that the elements are accessed in an order defined by the index rather than the backing storage.
let iterators = fields_to_index().map(|f| {
let field_name = f.ident.as_ref().unwrap();
let field_vis = &f.vis;
let field_name_string = field_name.to_string();
let error_msg = format!("Internal invariants broken, found empty slice in non_unique index '{field_name_string}'");
let iter_name = format_ident!(
Expand Down Expand Up @@ -342,7 +344,7 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
// First we maintain a reference to the backing store, so we can return references to the elements we are interested in.
// Second we maintain an optional inner_iter, only used for non-unique indexes. This is used to iterate through the Vec of matching elements for a given index value.
quote! {
pub(super) struct #iter_name<'a> {
#field_vis struct #iter_name<'a> {
_store_ref: &'a slab::Slab<#element_name>,
_iter: #iter_type,
_inner_iter: Option<core::slice::Iter<'a, usize>>,
Expand All @@ -358,64 +360,54 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
}
});

// Put Iterators and MultiIndexMap into a separate module to avoid polluting the namespace.
// Ensure this module has a name based upon the element name to allow multiple MultiIndexMaps in the same namespace.
let mod_name = format_ident!(
"multi_index_{}",
element_name
.to_string()
.to_case(convert_case::Case::Snake)
);
let element_vis = input.vis;

// Build the final output using quasi-quoting
let expanded = quote! {
mod #mod_name {
use super::*;
#[derive(Default, Clone)]
#element_vis struct #map_name {
_store: slab::Slab<#element_name>,
#(#lookup_table_fields)*
}

#[derive(Default, Clone)]
pub(super) struct #map_name {
_store: slab::Slab<#element_name>,
#(#lookup_table_fields)*
impl #map_name {
#element_vis fn len(&self) -> usize {
self._store.len()
}

impl #map_name {
pub(super) fn len(&self) -> usize {
self._store.len()
}

pub(super) fn is_empty(&self) -> bool {
self._store.is_empty()
}

pub(super) fn insert(&mut self, elem: #element_name) {
let idx = self._store.insert(elem);
let elem = &self._store[idx];
#element_vis fn is_empty(&self) -> bool {
self._store.is_empty()
}

#(#inserts)*
}
#element_vis fn insert(&mut self, elem: #element_name) {
let idx = self._store.insert(elem);
let elem = &self._store[idx];

pub(super) fn clear(&mut self) {
self._store.clear();
#(#clears)*
}
#(#inserts)*
}

// Allow iteration directly over the backing storage
pub(super) fn iter(&self) -> slab::Iter<#element_name> {
self._store.iter()
}
#element_vis fn clear(&mut self) {
self._store.clear();
#(#clears)*
}

// SAFETY:
// It is safe to mutate the non-indexed fields, however mutating any of the indexed fields will break the internal invariants.
// If the indexed fields need to be changed, the modify() method must be used.
pub(super) unsafe fn iter_mut(&mut self) -> slab::IterMut<#element_name> {
self._store.iter_mut()
}
// Allow iteration directly over the backing storage
#element_vis fn iter(&self) -> slab::Iter<#element_name> {
self._store.iter()
}

#(#accessors)*
// SAFETY:
// It is safe to mutate the non-indexed fields, however mutating any of the indexed fields will break the internal invariants.
// If the indexed fields need to be changed, the modify() method must be used.
#element_vis unsafe fn iter_mut(&mut self) -> slab::IterMut<#element_name> {
self._store.iter_mut()
}

#(#iterators)*
#(#accessors)*
}

#(#iterators)*

};

// Hand the output tokens back to the compiler.
Expand Down
1 change: 0 additions & 1 deletion tests/hashed_non_unique.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use multi_index_map::MultiIndexMap;
use multi_index_test_element::MultiIndexTestElementMap;

#[derive(Hash, PartialEq, Eq, Clone, Debug)]
struct TestNonPrimitiveType(u64);
Expand Down
1 change: 0 additions & 1 deletion tests/hashed_unique.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use multi_index_map::MultiIndexMap;
use multi_index_test_element::MultiIndexTestElementMap;

#[derive(Hash, PartialEq, Eq, Clone)]
struct TestNonPrimitiveType(u64);
Expand Down
3 changes: 0 additions & 3 deletions tests/mixed_non_unique.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use multi_index_map::MultiIndexMap;
use multi_index_multiple_hashed_non_unique_struct::MultiIndexMultipleHashedNonUniqueStructMap;
use multi_index_multiple_ordered_non_unique_struct::MultiIndexMultipleOrderedNonUniqueStructMap;
use multi_index_ordered_non_unique_and_hashed_non_unique_struct::MultiIndexOrderedNonUniqueAndHashedNonUniqueStructMap;

#[derive(MultiIndexMap, Clone)]
struct MultipleOrderedNonUniqueStruct {
Expand Down
1 change: 0 additions & 1 deletion tests/ordered_non_unique.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use multi_index_map::MultiIndexMap;
use multi_index_test_element::MultiIndexTestElementMap;

#[derive(Hash, PartialEq, Eq, Clone, Debug, PartialOrd, Ord)]
struct TestNonPrimitiveType(u64);
Expand Down
1 change: 0 additions & 1 deletion tests/ordered_unique.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use multi_index_map::MultiIndexMap;
use multi_index_test_element::MultiIndexTestElementMap;

#[derive(Hash, PartialEq, Eq, Clone, PartialOrd, Ord)]
struct TestNonPrimitiveType(u64);
Expand Down

0 comments on commit a086ff8

Please sign in to comment.