use crate::{store_value, StoredValue};
use std::{fmt, sync::Arc};
pub trait Callable<In: 'static, Out: 'static = ()> {
fn call(&self, input: In) -> Out;
}
pub struct Callback<In: 'static, Out: 'static = ()>(
StoredValue<Box<dyn Fn(In) -> Out>>,
);
impl<In> fmt::Debug for Callback<In> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt.write_str("Callback")
}
}
impl<In, Out> Clone for Callback<In, Out> {
fn clone(&self) -> Self {
*self
}
}
impl<In, Out> Copy for Callback<In, Out> {}
impl<In, Out> Callback<In, Out> {
pub fn new<F>(f: F) -> Callback<In, Out>
where
F: Fn(In) -> Out + 'static,
{
Self(store_value(Box::new(f)))
}
}
impl<In: 'static, Out: 'static> Callable<In, Out> for Callback<In, Out> {
fn call(&self, input: In) -> Out {
self.0.with_value(|f| f(input))
}
}
macro_rules! impl_from_fn {
($ty:ident) => {
#[cfg(not(feature = "nightly"))]
impl<F, In, T, Out> From<F> for $ty<In, Out>
where
F: Fn(In) -> T + 'static,
T: Into<Out> + 'static,
{
fn from(f: F) -> Self {
Self::new(move |x| f(x).into())
}
}
paste::paste! {
#[cfg(feature = "nightly")]
auto trait [<NotRaw $ty>] {}
#[cfg(feature = "nightly")]
impl<A, B> ![<NotRaw $ty>] for $ty<A, B> {}
#[cfg(feature = "nightly")]
impl<F, In, T, Out> From<F> for $ty<In, Out>
where
F: Fn(In) -> T + [<NotRaw $ty>] + 'static,
T: Into<Out> + 'static,
{
fn from(f: F) -> Self {
Self::new(move |x| f(x).into())
}
}
}
};
}
impl_from_fn!(Callback);
#[cfg(feature = "nightly")]
impl<In, Out> FnOnce<(In,)> for Callback<In, Out> {
type Output = Out;
extern "rust-call" fn call_once(self, args: (In,)) -> Self::Output {
Callable::call(&self, args.0)
}
}
#[cfg(feature = "nightly")]
impl<In, Out> FnMut<(In,)> for Callback<In, Out> {
extern "rust-call" fn call_mut(&mut self, args: (In,)) -> Self::Output {
Callable::call(&*self, args.0)
}
}
#[cfg(feature = "nightly")]
impl<In, Out> Fn<(In,)> for Callback<In, Out> {
extern "rust-call" fn call(&self, args: (In,)) -> Self::Output {
Callable::call(self, args.0)
}
}
pub struct SyncCallback<In: 'static, Out: 'static = ()>(
StoredValue<Arc<dyn Fn(In) -> Out>>,
);
impl<In> fmt::Debug for SyncCallback<In> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt.write_str("SyncCallback")
}
}
impl<In, Out> Callable<In, Out> for SyncCallback<In, Out> {
fn call(&self, input: In) -> Out {
self.0.with_value(|f| f(input))
}
}
impl<In, Out> Clone for SyncCallback<In, Out> {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl<In: 'static, Out: 'static> SyncCallback<In, Out> {
pub fn new<F>(fun: F) -> Self
where
F: Fn(In) -> Out + 'static,
{
Self(store_value(Arc::new(fun)))
}
}
impl_from_fn!(SyncCallback);
#[cfg(feature = "nightly")]
impl<In, Out> FnOnce<(In,)> for SyncCallback<In, Out> {
type Output = Out;
extern "rust-call" fn call_once(self, args: (In,)) -> Self::Output {
Callable::call(&self, args.0)
}
}
#[cfg(feature = "nightly")]
impl<In, Out> FnMut<(In,)> for SyncCallback<In, Out> {
extern "rust-call" fn call_mut(&mut self, args: (In,)) -> Self::Output {
Callable::call(&*self, args.0)
}
}
#[cfg(feature = "nightly")]
impl<In, Out> Fn<(In,)> for SyncCallback<In, Out> {
extern "rust-call" fn call(&self, args: (In,)) -> Self::Output {
Callable::call(self, args.0)
}
}
#[cfg(test)]
mod tests {
use crate::{
callback::{Callback, SyncCallback},
create_runtime,
};
struct NoClone {}
#[test]
fn clone_callback() {
let rt = create_runtime();
let callback = Callback::new(move |_no_clone: NoClone| NoClone {});
let _cloned = callback;
rt.dispose();
}
#[test]
fn clone_sync_callback() {
let rt = create_runtime();
let callback = SyncCallback::new(move |_no_clone: NoClone| NoClone {});
let _cloned = callback.clone();
rt.dispose();
}
#[test]
fn callback_from() {
let rt = create_runtime();
let _callback: Callback<(), String> = (|()| "test").into();
rt.dispose();
}
#[test]
fn callback_from_html() {
let rt = create_runtime();
use leptos::{
html::{AnyElement, HtmlElement},
*,
};
let _callback: Callback<String, HtmlElement<AnyElement>> =
(|x: String| {
view! {
<h1>{x}</h1>
}
})
.into();
rt.dispose();
}
#[test]
fn sync_callback_from() {
let rt = create_runtime();
let _callback: SyncCallback<(), String> = (|()| "test").into();
rt.dispose();
}
#[test]
fn sync_callback_from_html() {
use leptos::{
html::{AnyElement, HtmlElement},
*,
};
let rt = create_runtime();
let _callback: SyncCallback<String, HtmlElement<AnyElement>> =
(|x: String| {
view! {
<h1>{x}</h1>
}
})
.into();
rt.dispose();
}
}