Allow skipping a callback when reforming (#2864)

* Allow skipping a callback when reforming

This adds a method to the callback, similar to Rust's filter_map, which
allows to reform a callback, but also suppress the emit in case
the reform function returns `None`.

* Update packages/yew/src/callback.rs

Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com>

* Implement filter_reform for all Callback output types

As suggested in the PR, this implements filter_reform for all output
types of callbacks by mapping to `Option<OUT>`.

* Fix clippy error

Although I believe that the code is more readable/understandable with
an explicit map, I am not sure adding a clippy exception just for that
is justified. So I applied the clippy suggestion.

Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com>
This commit is contained in:
Jens Reimann 2022-09-15 01:14:58 +02:00 committed by GitHub
parent 81f7ea482b
commit beb0157c48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -68,7 +68,7 @@ impl<IN> Default for Callback<IN> {
impl<IN: 'static, OUT: 'static> Callback<IN, OUT> {
/// Creates a new callback from another callback and a function
/// That when emited will call that function and will emit the original callback
/// That when emitted will call that function and will emit the original callback
pub fn reform<F, T>(&self, func: F) -> Callback<T, OUT>
where
F: Fn(T) -> IN + 'static,
@ -80,6 +80,68 @@ impl<IN: 'static, OUT: 'static> Callback<IN, OUT> {
};
Callback::from(func)
}
/// Creates a new callback from another callback and a function.
/// When emitted will call the function and, only if it returns `Some(value)`, will emit
/// `value` to the original callback.
pub fn filter_reform<F, T>(&self, func: F) -> Callback<T, Option<OUT>>
where
F: Fn(T) -> Option<IN> + 'static,
{
let this = self.clone();
let func = move |input| func(input).map(|output| this.emit(output));
Callback::from(func)
}
}
impl<IN, OUT> ImplicitClone for Callback<IN, OUT> {}
#[cfg(test)]
mod test {
use std::sync::Mutex;
use super::*;
/// emit the callback with the provided value
fn emit<T, I, R: 'static + Clone, F, OUT>(values: I, f: F) -> Vec<R>
where
I: IntoIterator<Item = T>,
F: FnOnce(Callback<R, ()>) -> Callback<T, OUT>,
{
let result = Rc::new(Mutex::new(Vec::new()));
let cb_result = result.clone();
let cb = f(Callback::<R, ()>::from(move |v| {
cb_result.lock().unwrap().push(v);
}));
for value in values {
cb.emit(value);
}
let x = result.lock().unwrap().clone();
x
}
#[test]
fn test_callback() {
assert_eq!(*emit([true, false], |cb| cb), vec![true, false]);
}
#[test]
fn test_reform() {
assert_eq!(
*emit([true, false], |cb| cb.reform(|v: bool| !v)),
vec![false, true]
);
}
#[test]
fn test_filter_reform() {
assert_eq!(
*emit([1, 2, 3], |cb| cb.filter_reform(|v| match v {
1 => Some(true),
2 => Some(false),
_ => None,
})),
vec![true, false]
);
}
}