From beb0157c4865727bb350cefc57ddb76dfc6730bd Mon Sep 17 00:00:00 2001 From: Jens Reimann Date: Thu, 15 Sep 2022 01:14:58 +0200 Subject: [PATCH] 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 * 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`. * 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 --- packages/yew/src/callback.rs | 64 +++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/yew/src/callback.rs b/packages/yew/src/callback.rs index 84aff3ab3e3..68edd25a304 100644 --- a/packages/yew/src/callback.rs +++ b/packages/yew/src/callback.rs @@ -68,7 +68,7 @@ impl Default for Callback { impl Callback { /// 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(&self, func: F) -> Callback where F: Fn(T) -> IN + 'static, @@ -80,6 +80,68 @@ impl Callback { }; 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(&self, func: F) -> Callback> + where + F: Fn(T) -> Option + 'static, + { + let this = self.clone(); + let func = move |input| func(input).map(|output| this.emit(output)); + Callback::from(func) + } } impl ImplicitClone for Callback {} + +#[cfg(test)] +mod test { + use std::sync::Mutex; + + use super::*; + + /// emit the callback with the provided value + fn emit(values: I, f: F) -> Vec + where + I: IntoIterator, + F: FnOnce(Callback) -> Callback, + { + let result = Rc::new(Mutex::new(Vec::new())); + let cb_result = result.clone(); + let cb = f(Callback::::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] + ); + } +}