From d9af38c9e7adece67dd24d0bf1c1cf359779e88d Mon Sep 17 00:00:00 2001 From: Luke Randall Date: Thu, 23 Apr 2020 22:00:10 +0100 Subject: [PATCH 1/8] Convert Callback to be an enum to support Fn and FnOnce. --- yew/src/callback.rs | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/yew/src/callback.rs b/yew/src/callback.rs index 6be69d9c97a..2b31e8beb55 100644 --- a/yew/src/callback.rs +++ b/yew/src/callback.rs @@ -2,6 +2,7 @@ use std::fmt; use std::rc::Rc; +use std::cell::RefCell; /// Universal callback wrapper. /// /// `Rc` wrapper used to make it clonable. -pub struct Callback(Rc); + +pub enum Callback { + /// A callback that can be called multiple times + Callback(Rc), + /// A callback that will only be called once. Panics if it is called again + CallbackOnce(Rc>>>), +} impl From for Callback { fn from(func: F) -> Self { - Callback(Rc::new(func)) + Callback::Callback(Rc::new(func)) } } impl Clone for Callback { fn clone(&self) -> Self { - Callback(self.0.clone()) + match self { + Callback::Callback(cb) => Callback::Callback(cb.clone()), + Callback::CallbackOnce(cb) => Callback::CallbackOnce(cb.clone()), + } } } impl PartialEq for Callback { fn eq(&self, other: &Callback) -> bool { - Rc::ptr_eq(&self.0, &other.0) + match (&self, &other) { + (Callback::Callback(cb), Callback::Callback(other_cb)) => Rc::ptr_eq(cb, other_cb), + (Callback::CallbackOnce(cb), Callback::CallbackOnce(other_cb)) => Rc::ptr_eq(cb, other_cb), + _ => false, + } } } impl fmt::Debug for Callback { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Callback<_>") + let data = match self { + Callback::Callback(_) => "Callback<_>", + Callback::CallbackOnce(_) => "CallbackOnce<_>", + }; + + f.write_str(data) } } impl Callback { /// This method calls the actual callback. pub fn emit(&self, value: IN) { - (self.0)(value); + match self { + Callback::Callback(cb) => cb(value), + Callback::CallbackOnce(rc) => { + let cb = rc.replace(None); + let f = cb.expect("callback in CallbackOnce has already been used"); + f(value) + }, + }; } /// Creates a no-op callback which can be used when it is not suitable to use an From da59dc4445ce068957e57a3edf4c51745c0bba2b Mon Sep 17 00:00:00 2001 From: Luke Randall Date: Fri, 24 Apr 2020 11:02:22 +0100 Subject: [PATCH 2/8] Run cargo fmt --- yew/src/callback.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/yew/src/callback.rs b/yew/src/callback.rs index 2b31e8beb55..69b8d760b99 100644 --- a/yew/src/callback.rs +++ b/yew/src/callback.rs @@ -1,8 +1,8 @@ -//! This module contains structs to interact with `Scope`s. +//! This module contains data types for interacting with `Scope`s. +use std::cell::RefCell; use std::fmt; use std::rc::Rc; -use std::cell::RefCell; /// Universal callback wrapper. /// /// `Rc` wrapper used to make it clonable. - pub enum Callback { /// A callback that can be called multiple times Callback(Rc), /// A callback that will only be called once. Panics if it is called again - CallbackOnce(Rc>>>), + CallbackOnce(Rc>), } +type CallbackOnce = RefCell>>; + impl From for Callback { fn from(func: F) -> Self { Callback::Callback(Rc::new(func)) From 1e1bb8a49ed1f56033571a9917bf978365fea5fe Mon Sep 17 00:00:00 2001 From: Luke Randall Date: Fri, 24 Apr 2020 13:17:05 +0100 Subject: [PATCH 5/8] Add Callback::callback_once --- yew/src/callback.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/yew/src/callback.rs b/yew/src/callback.rs index 59b6715ac71..9930995da4c 100644 --- a/yew/src/callback.rs +++ b/yew/src/callback.rs @@ -71,6 +71,15 @@ impl Callback { }; } + /// Creates a callback from a FnOnce. You are responsible for ensuring + /// the callback is only called once otherwise it will panic. + pub fn callback_once(func: F) -> Self + where + F: FnOnce(IN) + 'static + { + Callback::CallbackOnce(Rc::new(RefCell::new(Some(Box::new(func))))) + } + /// Creates a no-op callback which can be used when it is not suitable to use an /// `Option`. pub fn noop() -> Self { From d00ab08fc7b68a2635f6e6fdda79790c69cfbb13 Mon Sep 17 00:00:00 2001 From: Luke Randall Date: Fri, 24 Apr 2020 13:17:21 +0100 Subject: [PATCH 6/8] Add Scope::callback_once --- yew/src/html/scope.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/yew/src/html/scope.rs b/yew/src/html/scope.rs index 25b8111beb7..c484d4706eb 100644 --- a/yew/src/html/scope.rs +++ b/yew/src/html/scope.rs @@ -119,7 +119,7 @@ impl Scope { self.update(ComponentUpdate::MessageBatch(messages)); } - /// This method creates a `Callback` which will send a message to the linked component's + /// Creates a `Callback` which will send a message to the linked component's /// update method when invoked. pub fn callback(&self, function: F) -> Callback where @@ -134,8 +134,23 @@ impl Scope { closure.into() } - /// This method creates a `Callback` which will send a batch of messages back to the linked - /// component's update method when called. + /// Creates a `Callback` from a FnOnce which will send a message to the linked + /// component's update method when invoked. + pub fn callback_once(&self, function: F) -> Callback + where + M: Into, + F: FnOnce(IN) -> M + 'static, + { + let scope = self.clone(); + let closure = move |input| { + let output = function(input); + scope.send_message(output); + }; + Callback::callback_once(closure) + } + + /// Creates a `Callback` which will send a batch of messages back to the linked + /// component's update method when invoked. pub fn batch_callback(&self, function: F) -> Callback where F: Fn(IN) -> Vec + 'static, From 7a838037bf43eecf0facee60fc93512e7b28afdd Mon Sep 17 00:00:00 2001 From: Luke Randall Date: Fri, 24 Apr 2020 13:28:10 +0100 Subject: [PATCH 7/8] Run cargo fmt --- yew/src/callback.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yew/src/callback.rs b/yew/src/callback.rs index 9930995da4c..aab489abdd8 100644 --- a/yew/src/callback.rs +++ b/yew/src/callback.rs @@ -75,7 +75,7 @@ impl Callback { /// the callback is only called once otherwise it will panic. pub fn callback_once(func: F) -> Self where - F: FnOnce(IN) + 'static + F: FnOnce(IN) + 'static, { Callback::CallbackOnce(Rc::new(RefCell::new(Some(Box::new(func))))) } From 1ef8736f63ca1116a9f54df7a5b00ba2bbf57078 Mon Sep 17 00:00:00 2001 From: Luke Randall Date: Fri, 24 Apr 2020 13:39:09 +0100 Subject: [PATCH 8/8] Rename to Callback::once --- yew/src/callback.rs | 2 +- yew/src/html/scope.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yew/src/callback.rs b/yew/src/callback.rs index aab489abdd8..eb466bf39c8 100644 --- a/yew/src/callback.rs +++ b/yew/src/callback.rs @@ -73,7 +73,7 @@ impl Callback { /// Creates a callback from a FnOnce. You are responsible for ensuring /// the callback is only called once otherwise it will panic. - pub fn callback_once(func: F) -> Self + pub fn once(func: F) -> Self where F: FnOnce(IN) + 'static, { diff --git a/yew/src/html/scope.rs b/yew/src/html/scope.rs index c484d4706eb..3b57d1c33b7 100644 --- a/yew/src/html/scope.rs +++ b/yew/src/html/scope.rs @@ -146,7 +146,7 @@ impl Scope { let output = function(input); scope.send_message(output); }; - Callback::callback_once(closure) + Callback::once(closure) } /// Creates a `Callback` which will send a batch of messages back to the linked