use std::marker::PhantomData;
use std::rc::Rc;
use super::signal::{Signal, create_stored};
pub struct Callback<Args = ()> {
inner: Signal<Rc<dyn Fn(Args)>>,
_args: PhantomData<Args>,
}
impl<Args> Clone for Callback<Args> {
fn clone(&self) -> Self {
*self
}
}
impl<Args> Copy for Callback<Args> {}
impl<Args: 'static> Callback<Args> {
fn from_rc(f: Rc<dyn Fn(Args)>) -> Self {
Self {
inner: create_stored(f),
_args: PhantomData,
}
}
pub fn run_packed(&self, args: Args) {
self.inner.with_untracked(|f| f(args));
}
}
impl<Args: 'static> Callback<Args> {
pub fn new<F: IntoCallback<Args>>(f: F) -> Self {
f.into_callback()
}
}
pub trait IntoCallback<Args> {
fn into_callback(self) -> Callback<Args>;
}
impl<F: Fn() + 'static> IntoCallback<()> for F {
fn into_callback(self) -> Callback<()> {
Callback::from_rc(Rc::new(move |()| self()))
}
}
impl<A: 'static, F: Fn(A) + 'static> IntoCallback<(A,)> for F {
fn into_callback(self) -> Callback<(A,)> {
Callback::from_rc(Rc::new(move |(a,)| self(a)))
}
}
impl<A: 'static, B: 'static, F: Fn(A, B) + 'static> IntoCallback<(A, B)> for F {
fn into_callback(self) -> Callback<(A, B)> {
Callback::from_rc(Rc::new(move |(a, b)| self(a, b)))
}
}
impl<A: 'static, B: 'static, C: 'static, F: Fn(A, B, C) + 'static> IntoCallback<(A, B, C)> for F {
fn into_callback(self) -> Callback<(A, B, C)> {
Callback::from_rc(Rc::new(move |(a, b, c)| self(a, b, c)))
}
}
impl Callback<()> {
pub fn run(&self) {
self.run_packed(());
}
}
impl<A: 'static> Callback<(A,)> {
pub fn run(&self, a: A) {
self.run_packed((a,));
}
}
impl<A: 'static, B: 'static> Callback<(A, B)> {
pub fn run(&self, a: A, b: B) {
self.run_packed((a, b));
}
}
impl<A: 'static, B: 'static, C: 'static> Callback<(A, B, C)> {
pub fn run(&self, a: A, b: B, c: C) {
self.run_packed((a, b, c));
}
}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use std::rc::Rc;
use super::*;
#[test]
fn a_callback_is_copy_and_callable_from_many_closures() {
let hits = Rc::new(Cell::new(0));
let hits_cb = hits.clone();
let cb = Callback::new(move || hits_cb.set(hits_cb.get() + 1));
let first = move || cb.run();
let second = move || cb.run();
first();
second();
cb.run();
assert_eq!(hits.get(), 3);
}
#[test]
fn arguments_stay_flat() {
let seen = Rc::new(Cell::new(0));
let seen_one = seen.clone();
let one = Callback::new(move |v: i32| seen_one.set(v));
one.run(7);
assert_eq!(seen.get(), 7);
let seen_two = seen.clone();
let two = Callback::new(move |a: i32, b: i32| seen_two.set(a * b));
two.run(6, 7);
assert_eq!(seen.get(), 42);
}
}