Skip to content

Commit

Permalink
Allow defining maps based on functions/closures
Browse files Browse the repository at this point in the history
Fixes #14
  • Loading branch information
JelteF committed Aug 17, 2023
1 parent eb84c41 commit 88cf8d8
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 2 deletions.
50 changes: 49 additions & 1 deletion src/btreemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::collections::BTreeMap;
use std::iter::{FromIterator, IntoIterator};
use std::ops::{Index, IndexMut};

use crate::DefaultFn;

/// A `BTreeMap` that returns a default when keys are accessed that are not present.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -13,7 +15,7 @@ pub struct DefaultBTreeMap<K: Eq + Ord, V> {
default: V,
#[debug(skip)]
#[cfg_attr(feature = "with-serde", serde(skip))]
default_fn: Box<dyn crate::DefaultFn<V>>,
default_fn: Box<dyn DefaultFn<V>>,
}

impl<K: Eq + Ord, V: PartialEq> PartialEq for DefaultBTreeMap<K, V> {
Expand Down Expand Up @@ -108,6 +110,31 @@ impl<K: Eq + Ord, V> DefaultBTreeMap<K, V> {
pub fn get_default(&self) -> V {
self.default_fn.call()
}

/// Creates an empty `DefaultBTreeMap` with `default_fn` as the default value generation
/// function for missing keys. When the provided `default_fn` only calls clone on a value,
/// using `DefaultBTreeMap::new` is preferred.
pub fn from_fn(default_fn: impl DefaultFn<V> + 'static) -> DefaultBTreeMap<K, V> {
DefaultBTreeMap {
map: BTreeMap::new(),
default: default_fn.call(),
default_fn: Box::new(default_fn),
}
}

/// Creates a `DefaultBTreeMap` based on an existing map and using `default_fn` as the default
/// value generation function for missing keys. When the provided `default_fn` is equivalent to
/// V::default(), then using `DefaultBTreeMap::from(map)` is preferred.
pub fn from_map_and_fn(
map: BTreeMap<K, V>,
default_fn: impl DefaultFn<V> + 'static,
) -> DefaultBTreeMap<K, V> {
DefaultBTreeMap {
map,
default: default_fn.call(),
default_fn: Box::new(default_fn),
}
}
}

impl<K: Eq + Ord, V> DefaultBTreeMap<K, V> {
Expand Down Expand Up @@ -424,6 +451,27 @@ mod tests {
assert_eq!(expected, default.into());
}

#[test]
fn from_fn() {
let i: i32 = 20;
let mut map = DefaultBTreeMap::from_fn(move || i);
map[0] += 1;
assert_eq!(21, map[0]);
assert_eq!(20, map[1]);

}

#[test]
fn from_map_and_fn() {
let i: i32 = 20;
let normal: BTreeMap<i32, i32> = vec![(0, 1), (2, 3)].into_iter().collect();
let mut map = DefaultBTreeMap::from_map_and_fn(normal, move || i);
map[0] += 1;
assert_eq!(map[0], 2);
assert_eq!(map[1], 20);
assert_eq!(map[2], 3);
}

#[cfg(feature = "with-serde")]
mod serde_tests {
use super::*;
Expand Down
50 changes: 49 additions & 1 deletion src/hashmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use std::hash::Hash;
use std::iter::{FromIterator, IntoIterator};
use std::ops::{Index, IndexMut};

use crate::DefaultFn;

/// A `HashMap` that returns a default when keys are accessed that are not present.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -14,7 +16,7 @@ pub struct DefaultHashMap<K: Eq + Hash, V> {
default: V,
#[debug(skip)]
#[cfg_attr(feature = "with-serde", serde(skip))]
default_fn: Box<dyn crate::DefaultFn<V>>,
default_fn: Box<dyn DefaultFn<V>>,
}

impl<K: Eq + Hash, V: PartialEq> PartialEq for DefaultHashMap<K, V> {
Expand Down Expand Up @@ -109,6 +111,31 @@ impl<K: Eq + Hash, V> DefaultHashMap<K, V> {
pub fn get_default(&self) -> V {
self.default_fn.call()
}

/// Creates an empty `DefaultHashMap` with `default_fn` as the default value generation
/// function for missing keys. When the provided `default_fn` only calls clone on a value,
/// using `DefaultHashMap::new` is preferred.
pub fn from_fn(default_fn: impl DefaultFn<V> + 'static) -> DefaultHashMap<K, V> {
DefaultHashMap {
map: HashMap::new(),
default: default_fn.call(),
default_fn: Box::new(default_fn),
}
}

/// Creates a `DefaultHashMap` based on an existing map and using `default_fn` as the default
/// value generation function for missing keys. When the provided `default_fn` is equivalent to
/// V::default(), then using `DefaultHashMap::from(map)` is preferred.
pub fn from_map_and_fn(
map: HashMap<K, V>,
default_fn: impl DefaultFn<V> + 'static,
) -> DefaultHashMap<K, V> {
DefaultHashMap {
map,
default: default_fn.call(),
default_fn: Box::new(default_fn),
}
}
}

impl<K: Eq + Hash, V> DefaultHashMap<K, V> {
Expand Down Expand Up @@ -441,6 +468,27 @@ mod tests {
assert_eq!(expected, default.into());
}

#[test]
fn from_fn() {
let i: i32 = 20;
let mut map = DefaultHashMap::from_fn(move || i);
map[0] += 1;
assert_eq!(21, map[0]);
assert_eq!(20, map[1]);

}

#[test]
fn from_map_and_fn() {
let i: i32 = 20;
let normal: HashMap<i32, i32> = vec![(0, 1), (2, 3)].into_iter().collect();
let mut map = DefaultHashMap::from_map_and_fn(normal, move || i);
map[0] += 1;
assert_eq!(map[0], 2);
assert_eq!(map[1], 20);
assert_eq!(map[2], 3);
}

#[cfg(feature = "with-serde")]
mod serde_tests {
use super::*;
Expand Down

0 comments on commit 88cf8d8

Please sign in to comment.