Skip to content

Commit

Permalink
feat(strategy): implement merge strategies for BTreeMap (#13)
Browse files Browse the repository at this point in the history
* feat: implement strategies for btreemap

Signed-off-by: simonsan <[email protected]>

* fix: clippy

Signed-off-by: simonsan <[email protected]>

* change method names

Signed-off-by: simonsan <[email protected]>

* adapt test names

Signed-off-by: simonsan <[email protected]>

* corrections

---------

Signed-off-by: simonsan <[email protected]>
Co-authored-by: Alexander Weiss <[email protected]>
  • Loading branch information
simonsan and aawsome authored Oct 3, 2024
1 parent edb381b commit 4ba9454
Show file tree
Hide file tree
Showing 26 changed files with 206 additions and 68 deletions.
38 changes: 38 additions & 0 deletions crates/conflate/src/btreemap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! Merge strategies for `BTreeMap`s.
//!
//! These strategies are only available if the `std` feature is enabled.
use std::collections::BTreeMap;

/// Append values, on conflict, overwrite elements of `left` with `right`.
///
/// In other words, this gives precedence to `right`.
pub fn append_or_overwrite<K: Eq + Ord, V>(left: &mut BTreeMap<K, V>, right: BTreeMap<K, V>) {
left.extend(right)
}

/// Append values, on conflict, ignore elements from `right`.
///
/// In other words, this gives precedence to `left`.
pub fn append_or_ignore<K: Eq + Ord, V>(left: &mut BTreeMap<K, V>, right: BTreeMap<K, V>) {
for (k, v) in right {
left.entry(k).or_insert(v);
}
}

/// Append values, on conflict, recursively merge the elements.
pub fn append_or_recurse<K: Eq + Ord, V: crate::Merge>(
left: &mut BTreeMap<K, V>,
right: BTreeMap<K, V>,
) {
use std::collections::btree_map::Entry;

for (k, v) in right {
match left.entry(k) {
Entry::Occupied(mut existing) => existing.get_mut().merge(v),
Entry::Vacant(empty) => {
empty.insert(v);
}
}
}
}
2 changes: 2 additions & 0 deletions crates/conflate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ pub use conflate_derive::*;

pub mod bool;
#[cfg(feature = "std")]
pub mod btreemap;
#[cfg(feature = "std")]
pub mod hashmap;
#[cfg(feature = "num")]
pub mod num;
Expand Down
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions crates/conflate/tests/compile/derive-enum.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: conflate::Merge can only be derived for structs
--> tests/compile/derive-enum.rs:6:10
|
6 | #[derive(Merge)]
| ^^^^^
|
= note: this error originates in the derive macro `Merge` (in Nightly builds, run with -Z macro-backtrace for more info)
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error[E0308]: arguments to this function are incorrect
--> tests/compile/derive-invalid-default-strategy.rs:7:20
|
7 | #[merge(strategy = my_custom_merge_strategy)]
| ____________________-^^^^^^^^^^^^^^^^^^^^^^^
8 | | struct S {
9 | | field1: u16,
| | -
| | |
| |__________expected `&mut u8`, found `&mut u16`
| expected `u8`, found `u16`
|
= note: expected mutable reference `&mut u8`
found mutable reference `&mut u16`
note: function defined here
--> tests/compile/derive-invalid-default-strategy.rs:12:4
|
12 | fn my_custom_merge_strategy(left: &mut u8, right: u8) {
| ^^^^^^^^^^^^^^^^^^^^^^^^ ------------- ---------
help: you can convert a `u16` to a `u8` and panic if the converted value doesn't fit
|
9 | field1.try_into().unwrap(): u16,
| ++++++++++++++++++++
File renamed without changes.
File renamed without changes.
File renamed without changes.
20 changes: 20 additions & 0 deletions crates/conflate/tests/compile/derive-invalid-strategy.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0308]: mismatched types
--> tests/compile/derive-invalid-strategy.rs:8:24
|
8 | #[merge(strategy = my_custom_merge_strategy)]
| ^-----------------------
| |
| ________________________arguments to this function are incorrect
| |
9 | | field1: u8,
| |__________^ expected `u8`, found `&mut u8`
|
note: function defined here
--> tests/compile/derive-invalid-strategy.rs:12:4
|
12 | fn my_custom_merge_strategy(left: u8, right: u8) -> u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^ --------
help: consider removing the borrow
|
8 | #[merge(strategy = my_custom_merge_strategy)]
|
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions crates/conflate/tests/compile/derive-no-strategy.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: expected `=`
--> tests/compile/derive-no-strategy.rs:8:21
|
8 | #[merge(strategy)]
| ^
File renamed without changes.
15 changes: 15 additions & 0 deletions crates/conflate/tests/compile/derive-u8.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0277]: the trait bound `Option<u8>: Merge` is not satisfied
--> tests/compile/derive-u8.rs:8:5
|
8 | field1: Option<u8>,
| ^^^^^^ the trait `Merge` is not implemented for `Option<u8>`
|
= help: the trait `Merge` is implemented for `S`

error[E0277]: the trait bound `u8: Merge` is not satisfied
--> tests/compile/derive-u8.rs:9:5
|
9 | field2: u8,
| ^^^^^^ the trait `Merge` is not implemented for `u8`
|
= help: the trait `Merge` is implemented for `S`
3 changes: 2 additions & 1 deletion tests/derive.rs → crates/conflate/tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ fn test_strategy_usize_add() {
}

fn add(left: &mut usize, right: usize) {
*left = *left + right;
*left += right;
}

test(S::new(0), S::new(0), S::new(0));
Expand Down Expand Up @@ -551,6 +551,7 @@ fn test_unnamed_fields_skip() {
}

#[test]
#[allow(dead_code)]
fn test_default_strategy() {
#[derive(Debug, Merge, PartialEq)]
struct N(#[merge(strategy = conflate::num::saturating_add)] u8);
Expand Down
94 changes: 94 additions & 0 deletions tests/strategies.rs → crates/conflate/tests/strategies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,97 @@ mod hashmap {
);
}
}

#[cfg(feature = "std")]
mod btreemap {
use super::test;
use crate::Merge;
use std::collections::BTreeMap;

/// A macro to create a BTreeMap.
///
/// Example:
///
/// ```
/// let letters = btreemap!{"a" => "b", "c" => "d"};
/// ```
///
/// Trailing commas are allowed.
/// Commas between elements are required (even if the expression is a block).
macro_rules! btreemap {
($( $key: expr => $val: expr ),* $(,)*) => {{
let mut btreemap = BTreeMap::default();
$( btreemap.insert($key, $val); )*
btreemap
}}
}

#[test]
fn test_append_or_overwrite() {
#[derive(Debug, Merge, PartialEq)]
struct S(#[merge(strategy = conflate::btreemap::append_or_overwrite)] BTreeMap<u8, u8>);

test(
S(btreemap! {1 => 2}),
S(btreemap! {1 => 1}),
S(btreemap! {1 => 2}),
);
test(
S(btreemap! {1 => 1}),
S(btreemap! {1 => 2}),
S(btreemap! {1 => 1}),
);
test(
S(btreemap! {0 => 1, 1 => 2}),
S(btreemap! {0 => 1}),
S(btreemap! {1 => 2}),
);
}

#[test]
fn test_append_or_ignore() {
#[derive(Debug, Merge, PartialEq)]
struct S(#[merge(strategy = conflate::btreemap::append_or_ignore)] BTreeMap<u8, u8>);

test(
S(btreemap! {1 => 1}),
S(btreemap! {1 => 1}),
S(btreemap! {1 => 2}),
);
test(
S(btreemap! {1 => 2}),
S(btreemap! {1 => 2}),
S(btreemap! {1 => 1}),
);
test(
S(btreemap! {0 => 1, 1 => 2}),
S(btreemap! {0 => 1}),
S(btreemap! {1 => 2}),
);
}

#[test]
fn test_append_or_recurse() {
#[derive(Debug, Merge, PartialEq)]
struct N(#[merge(strategy = conflate::num::saturating_add)] u8);

#[derive(Debug, Merge, PartialEq)]
struct S(#[merge(strategy = conflate::btreemap::append_or_recurse)] BTreeMap<u8, N>);

test(
S(btreemap! {1 => N(3)}),
S(btreemap! {1 => N(1)}),
S(btreemap! {1 => N(2)}),
);
test(
S(btreemap! {1 => N(3)}),
S(btreemap! {1 => N(2)}),
S(btreemap! {1 => N(1)}),
);
test(
S(btreemap! {0 => N(1), 1 => N(2)}),
S(btreemap! {0 => N(1)}),
S(btreemap! {1 => N(2)}),
);
}
}
7 changes: 0 additions & 7 deletions tests/compile/derive-enum.stderr

This file was deleted.

27 changes: 0 additions & 27 deletions tests/compile/derive-invalid-default-strategy.stderr

This file was deleted.

13 changes: 0 additions & 13 deletions tests/compile/derive-invalid-strategy.stderr

This file was deleted.

5 changes: 0 additions & 5 deletions tests/compile/derive-no-strategy.stderr

This file was deleted.

15 changes: 0 additions & 15 deletions tests/compile/derive-u8.stderr

This file was deleted.

0 comments on commit 4ba9454

Please sign in to comment.