From fb6a6662573a2857564f151a9597eae6549b5451 Mon Sep 17 00:00:00 2001 From: Jens Reimann Date: Thu, 8 Sep 2022 13:42:43 +0200 Subject: [PATCH 1/4] 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`. --- packages/yew/src/callback.rs | 68 +++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/packages/yew/src/callback.rs b/packages/yew/src/callback.rs index 84aff3ab3e3..55fc5287bc4 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, @@ -82,4 +82,70 @@ impl Callback { } } +impl Callback { + /// Creates a new callback from another callback and a function + /// That when emitted will call that function and will emit the original callback if it is + /// not `None`. + pub fn filter_reform(&self, func: F) -> Callback + where + F: Fn(T) -> Option + 'static, + { + let this = self.clone(); + let func = move |input| { + if let Some(output) = func(input) { + 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] + ); + } +} From 75fc5837e2b877041782f07d8a5866af63957fd8 Mon Sep 17 00:00:00 2001 From: Jens Reimann Date: Fri, 9 Sep 2022 09:42:35 +0200 Subject: [PATCH 2/4] Update packages/yew/src/callback.rs Co-authored-by: WorldSEnder --- packages/yew/src/callback.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/yew/src/callback.rs b/packages/yew/src/callback.rs index 55fc5287bc4..971acb3d960 100644 --- a/packages/yew/src/callback.rs +++ b/packages/yew/src/callback.rs @@ -83,9 +83,9 @@ impl Callback { } impl Callback { - /// Creates a new callback from another callback and a function - /// That when emitted will call that function and will emit the original callback if it is - /// not `None`. + /// 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, From 291beaf8de84b32ef99c43380754f6f07236e9ba Mon Sep 17 00:00:00 2001 From: Jens Reimann Date: Fri, 9 Sep 2022 09:56:32 +0200 Subject: [PATCH 3/4] 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`. --- packages/yew/src/callback.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/yew/src/callback.rs b/packages/yew/src/callback.rs index 971acb3d960..74982930717 100644 --- a/packages/yew/src/callback.rs +++ b/packages/yew/src/callback.rs @@ -80,21 +80,18 @@ impl Callback { }; Callback::from(func) } -} -impl Callback { /// 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 + pub fn filter_reform(&self, func: F) -> Callback> where F: Fn(T) -> Option + 'static, { let this = self.clone(); - let func = move |input| { - if let Some(output) = func(input) { - this.emit(output); - } + let func = move |input| match func(input) { + Some(output) => Some(this.emit(output)), + None => None, }; Callback::from(func) } @@ -109,14 +106,16 @@ mod test { use super::*; /// emit the callback with the provided value - fn emit(values: I, f: F) -> Vec + fn emit(values: I, f: F) -> Vec where I: IntoIterator, - F: FnOnce(Callback) -> Callback, + 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))); + let cb = f(Callback::::from(move |v| { + cb_result.lock().unwrap().push(v); + })); for value in values { cb.emit(value); } From 041e4bb1a4473cc78b2ef8fac386039069a8dc71 Mon Sep 17 00:00:00 2001 From: Jens Reimann Date: Mon, 12 Sep 2022 13:53:33 +0200 Subject: [PATCH 4/4] 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. --- packages/yew/src/callback.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/yew/src/callback.rs b/packages/yew/src/callback.rs index 74982930717..68edd25a304 100644 --- a/packages/yew/src/callback.rs +++ b/packages/yew/src/callback.rs @@ -89,10 +89,7 @@ impl Callback { F: Fn(T) -> Option + 'static, { let this = self.clone(); - let func = move |input| match func(input) { - Some(output) => Some(this.emit(output)), - None => None, - }; + let func = move |input| func(input).map(|output| this.emit(output)); Callback::from(func) } }