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

impl Map::retain and Set::retain #27

Merged
merged 4 commits into from
Oct 14, 2022
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
38 changes: 30 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,40 @@ name: CI
on: [push, pull_request]

jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with: { toolchain: beta, override: true, profile: minimal, components: clippy }
- run: cargo clippy --workspace --all-features --tests --examples --benches -- --deny warnings

rustfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with: { toolchain: beta, override: true, profile: minimal, components: rustfmt }
- run: cargo fmt --check

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: beta
override: true
profile: minimal
components: clippy, rustfmt
with: { toolchain: beta, override: true, profile: minimal }
- run: cargo build --all-features --examples --tests --benches

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with: { toolchain: beta, override: true, profile: minimal }
# Test all relevant feature combos:
# features: std, map, serde
- run: cargo test --all-features
# features: -std, -map, -serde
- run: cargo test --no-default-features
- run: cargo clippy --workspace --all-features --tests --examples --benches -- --deny warnings
- run: cargo fmt --check
# features: -std, -map, serde
- run: cargo test --no-default-features --features serde
20 changes: 20 additions & 0 deletions fixed-map-derive/src/any_variants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub(crate) fn implement(cx: &Ctxt, en: &DataEnum) -> Result<TokenStream, ()> {
let mut get_mut = Vec::new();
let mut insert = Vec::new();
let mut remove = Vec::new();
let mut retain = Vec::new();
let mut clear = Vec::new();
let mut copy_bounds = Vec::new();
let mut field_specs = Vec::new();
Expand Down Expand Up @@ -64,6 +65,14 @@ pub(crate) fn implement(cx: &Ctxt, en: &DataEnum) -> Result<TokenStream, ()> {
get_mut.push(quote!(#option::as_mut(&mut self.#name)));
insert.push(quote!(#mem::replace(&mut self.#name, #option::Some(value))));
remove.push(quote!(#mem::replace(&mut self.#name, #option::None)));
retain.push(quote! {
if let Some(val) = #option::as_mut(&mut self.#name) {
if !func(#ident::#var, val) {
self.#name = None;
}
}
});

FieldKind::Simple
}
Fields::Unnamed(unnamed) => {
Expand Down Expand Up @@ -91,6 +100,9 @@ pub(crate) fn implement(cx: &Ctxt, en: &DataEnum) -> Result<TokenStream, ()> {
get_mut.push(quote!(#as_storage::get_mut(&mut self.#name, v)));
insert.push(quote!(#as_storage::insert(&mut self.#name, v, value)));
remove.push(quote!(#as_storage::remove(&mut self.#name, v)));
retain.push(quote! {
#as_storage::retain(&mut self.#name, |k, v| func(#ident::#var(k), v));
});

copy_bounds.push(quote!(#storage: #copy));

Expand Down Expand Up @@ -220,6 +232,14 @@ pub(crate) fn implement(cx: &Ctxt, en: &DataEnum) -> Result<TokenStream, ()> {
}
}

#[inline]
fn retain<F>(&mut self, mut func: F)
where
F: FnMut(#ident, &mut V) -> bool
{
#(#retain)*
}

#[inline]
fn clear(&mut self) {
#(#clear;)*
Expand Down
17 changes: 17 additions & 0 deletions fixed-map-derive/src/unit_variants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) fn implement(cx: &Ctxt, en: &DataEnum) -> Result<TokenStream, ()> {
let mut get_mut = Vec::new();
let mut insert = Vec::new();
let mut remove = Vec::new();
let mut retain = Vec::new();
let mut keys_iter_init = Vec::new();
let mut iter_init = Vec::new();

Expand All @@ -59,6 +60,13 @@ pub(crate) fn implement(cx: &Ctxt, en: &DataEnum) -> Result<TokenStream, ()> {
get_mut.push(quote!(#option::as_mut(#name)));
insert.push(quote!(#mem::replace(#name, #option::Some(value))));
remove.push(quote!(#mem::take(#name)));
retain.push(quote! {
if let Some(val) = #option::as_mut(#name) {
if !func(#ident::#var, val) {
*#name = None;
}
}
});
keys_iter_init.push(quote!(if #name.is_some() { Some(#ident::#var) } else { None }));
iter_init.push(quote!((#ident::#var, #name)));
names.push(name.clone());
Expand Down Expand Up @@ -180,6 +188,15 @@ pub(crate) fn implement(cx: &Ctxt, en: &DataEnum) -> Result<TokenStream, ()> {
}
}

#[inline]
fn retain<F>(&mut self, mut func: F)
where
F: FnMut(#ident, &mut V) -> bool
{
let [#(#names),*] = &mut self.data;
#(#retain)*
}

#[inline]
fn clear(&mut self) {
self.data = [#(#field_inits),*];
Expand Down
70 changes: 70 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,76 @@ where
self.storage.remove(key)
}

/// Retains only the elements specified by the predicate.
///
/// In other words, remove all pairs (k, v) for which f(k, &mut v) returns false.
/// The elements are visited in unsorted (and unspecified) order.
///
/// # Examples
///
/// ```
/// use fixed_map::{Key, Map};
///
/// #[derive(Clone, Copy, Key)]
/// enum Key {
/// First,
/// Second,
/// }
///
/// let mut map: Map<Key, i32> = Map::new();
///
/// map.insert(Key::First, 42);
/// map.insert(Key::Second, -10);
///
/// map.retain(|k, v| *v > 0);
///
/// assert_eq!(map.len(), 1);
/// assert_eq!(map.get(Key::First), Some(&42));
/// assert_eq!(map.get(Key::Second), None);
/// ```
///
/// Using a composite key:
///
/// ```
/// use fixed_map::{Key, Map};
///
/// #[derive(Clone, Copy, Key)]
/// enum Key {
/// First(bool),
/// Second,
/// }
///
/// let mut map: Map<Key, i32> = Map::new();
///
/// map.insert(Key::First(true), 42);
/// map.insert(Key::First(false), -31);
/// map.insert(Key::Second, 100);
///
/// let mut other = map.clone();
/// assert_eq!(map.len(), 3);
///
/// map.retain(|k, v| *v > 0);
///
/// assert_eq!(map.len(), 2);
/// assert_eq!(map.get(Key::First(true)), Some(&42));
/// assert_eq!(map.get(Key::First(false)), None);
/// assert_eq!(map.get(Key::Second), Some(&100));
///
/// other.retain(|k, v| matches!(k, Key::First(_)));
///
/// assert_eq!(other.len(), 2);
/// assert_eq!(other.get(Key::First(true)), Some(&42));
/// assert_eq!(other.get(Key::First(false)), Some(&-31));
/// assert_eq!(other.get(Key::Second), None);
/// ```
#[inline]
pub fn retain<F>(&mut self, f: F)
where
F: FnMut(K, &mut V) -> bool,
{
self.storage.retain(f);
}

/// Clears the map, removing all key-value pairs. Keeps the allocated memory
/// for reuse.
///
Expand Down
73 changes: 73 additions & 0 deletions src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,79 @@ where
self.storage.remove(key).is_some()
}

/// Retains only the elements specified by the predicate.
///
/// In other words, remove all elements e for which f(e) returns false.
/// The elements are visited in unsorted (and unspecified) order.
///
/// # Examples
///
/// ```
/// use fixed_map::{Key, Set};
///
/// #[derive(Clone, Copy, Key)]
/// enum Key {
/// First,
/// Second,
/// }
///
/// let mut set = Set::new();
///
/// set.insert(Key::First);
/// set.insert(Key::Second);
///
/// set.retain(|k| matches!(k, Key::First));
///
/// assert_eq!(set.len(), 1);
/// assert_eq!(set.contains(Key::First), true);
/// assert_eq!(set.contains(Key::Second), false);
/// ```
///
/// Using a composite key:
///
/// ```
/// use fixed_map::{Key, Set};
///
/// #[derive(Clone, Copy, Key)]
/// enum Key {
/// First(bool),
/// Second(bool),
/// }
///
/// let mut set = Set::new();
///
/// set.insert(Key::First(true));
/// set.insert(Key::First(false));
/// set.insert(Key::Second(true));
/// set.insert(Key::Second(false));
///
/// let mut other = set.clone();
/// assert_eq!(set.len(), 4);
///
/// set.retain(|k| matches!(k, Key::First(true) | Key::Second(true)));
///
/// assert_eq!(set.len(), 2);
/// assert_eq!(set.contains(Key::First(true)), true);
/// assert_eq!(set.contains(Key::First(false)), false);
/// assert_eq!(set.contains(Key::Second(true)), true);
/// assert_eq!(set.contains(Key::Second(false)), false);
///
/// other.retain(|k| matches!(k, Key::First(_)));
///
/// assert_eq!(other.len(), 2);
/// assert_eq!(other.contains(Key::First(true)), true);
/// assert_eq!(other.contains(Key::First(false)), true);
/// assert_eq!(other.contains(Key::Second(true)), false);
/// assert_eq!(other.contains(Key::Second(false)), false);
/// ```
#[inline]
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(K) -> bool,
{
self.storage.retain(|k, _| f(k));
}

/// Clears the set, removing all values.
///
/// # Examples
Expand Down
5 changes: 5 additions & 0 deletions src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ pub trait Storage<K, V>: Default {
/// This is the storage abstraction for [`Map::remove`][crate::Map::remove].
fn remove(&mut self, key: K) -> Option<V>;

/// This is the storage abstraction for [`Map::retain`][crate::Map::retain].
fn retain<F>(&mut self, f: F)
where
F: FnMut(K, &mut V) -> bool;

/// This is the storage abstraction for [`Map::clear`][crate::Map::clear].
fn clear(&mut self);

Expand Down
17 changes: 17 additions & 0 deletions src/storage/boolean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,23 @@ impl<V> Storage<bool, V> for BooleanStorage<V> {
}
}

#[inline]
fn retain<F>(&mut self, mut func: F)
where
F: FnMut(bool, &mut V) -> bool,
{
if let Some(t) = self.t.as_mut() {
if !func(true, t) {
self.t = None;
}
}
if let Some(f) = self.f.as_mut() {
if !func(false, f) {
self.f = None;
}
}
}

#[inline]
fn clear(&mut self) {
self.t = None;
Expand Down
8 changes: 8 additions & 0 deletions src/storage/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ where
self.inner.remove(&key)
}

#[inline]
fn retain<F>(&mut self, mut func: F)
where
F: FnMut(K, &mut V) -> bool,
{
self.inner.retain(|&k, v| func(k, v));
}

#[inline]
fn clear(&mut self) {
self.inner.clear();
Expand Down
13 changes: 13 additions & 0 deletions src/storage/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,19 @@ where
}
}

#[inline]
fn retain<F>(&mut self, mut func: F)
where
F: FnMut(Option<K>, &mut V) -> bool,
{
self.some.retain(|k, v| func(Some(k), v));
if let Some(none) = self.none.as_mut() {
if !func(None, none) {
self.none = None;
}
}
}

#[inline]
fn clear(&mut self) {
self.some.clear();
Expand Down
12 changes: 12 additions & 0 deletions src/storage/singleton.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ where
mem::replace(&mut self.inner, None)
}

#[inline]
fn retain<F>(&mut self, mut func: F)
where
F: FnMut(K, &mut V) -> bool,
{
if let Some(val) = self.inner.as_mut() {
if !func(K::default(), val) {
self.inner = None;
}
}
}

#[inline]
fn clear(&mut self) {
self.inner = None;
Expand Down
3 changes: 2 additions & 1 deletion examples/empty.rs → tests/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use fixed_map::{Key, Map};
#[derive(Debug, Clone, Copy, Key)]
enum Key {}

fn main() {
#[test]
fn empty() {
let _ = Map::<Key, u32>::new();
}
Loading