use std::fmt;
use std::sync::Arc;
use crate::effect::EffectDependencies;
use crate::hook_context::with_hook_context;
pub struct Callback<IN, OUT = ()> {
callback: Arc<dyn Fn(IN) -> OUT + Send + Sync>,
}
impl<IN, OUT> Clone for Callback<IN, OUT> {
fn clone(&self) -> Self {
Self {
callback: self.callback.clone(),
}
}
}
impl<IN, OUT> Callback<IN, OUT> {
pub fn new<F>(func: F) -> Self
where
F: Fn(IN) -> OUT + Send + Sync + 'static,
{
Self {
callback: Arc::new(func),
}
}
pub fn emit(&self, input: IN) -> OUT {
(self.callback)(input)
}
pub fn reform<F, T>(&self, func: F) -> Callback<T, OUT>
where
F: Fn(T) -> IN + Send + Sync + 'static,
IN: 'static,
OUT: 'static,
{
let callback = self.callback.clone();
Callback::new(move |input: T| {
let transformed = func(input);
callback(transformed)
})
}
pub fn filter_reform<F, T>(&self, func: F) -> Callback<T, Option<OUT>>
where
F: Fn(T) -> Option<IN> + Send + Sync + 'static,
IN: 'static,
OUT: 'static,
{
let callback = self.callback.clone();
Callback::new(move |input: T| func(input).map(|v| callback(v)))
}
}
impl<IN> Callback<IN> {
pub fn noop() -> Self {
Self::new(|_| {})
}
}
impl<IN, OUT> Default for Callback<IN, OUT>
where
OUT: Default,
{
fn default() -> Self {
Self::new(|_| OUT::default())
}
}
impl<IN, OUT, F> From<F> for Callback<IN, OUT>
where
F: Fn(IN) -> OUT + Send + Sync + 'static,
{
fn from(func: F) -> Self {
Self::new(func)
}
}
impl<IN, OUT> From<Arc<dyn Fn(IN) -> OUT + Send + Sync>> for Callback<IN, OUT> {
fn from(func: Arc<dyn Fn(IN) -> OUT + Send + Sync>) -> Self {
Self { callback: func }
}
}
impl<IN> From<Option<Callback<IN>>> for Callback<IN> {
fn from(opt: Option<Callback<IN>>) -> Self {
opt.unwrap_or_else(Self::noop)
}
}
impl<IN, OUT> fmt::Debug for Callback<IN, OUT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Callback")
.field("callback", &"<function>")
.finish()
}
}
impl<IN, OUT> PartialEq for Callback<IN, OUT> {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.callback, &other.callback)
}
}
pub trait IntoCallback<IN, OUT = ()> {
fn into_callback(self) -> Callback<IN, OUT>;
}
impl<IN, OUT, F> IntoCallback<IN, OUT> for F
where
F: Fn(IN) -> OUT + Send + Sync + 'static,
{
fn into_callback(self) -> Callback<IN, OUT> {
Callback::from(self)
}
}
impl<IN, OUT> IntoCallback<IN, OUT> for Callback<IN, OUT> {
fn into_callback(self) -> Callback<IN, OUT> {
self
}
}
pub trait IntoCallbackProp<T> {
fn into_callback_prop(self) -> T;
}
impl<F, IN, OUT> IntoCallbackProp<Callback<IN, OUT>> for F
where
F: IntoCallback<IN, OUT>,
{
fn into_callback_prop(self) -> Callback<IN, OUT> {
self.into_callback()
}
}
impl<F, IN, OUT> IntoCallbackProp<Option<Callback<IN, OUT>>> for F
where
F: IntoCallback<IN, OUT>,
{
fn into_callback_prop(self) -> Option<Callback<IN, OUT>> {
Some(self.into_callback())
}
}
impl<IN, OUT> Callback<IN, OUT>
where
IN: 'static,
{
pub fn from_fn<F>(func: F) -> Self
where
F: Fn(IN) -> OUT + Send + Sync + 'static,
{
Self::from(func)
}
pub fn constant(value: OUT) -> Self
where
OUT: Clone + Send + Sync + 'static,
{
Self::from(move |_| value.clone())
}
pub fn debug() -> Self
where
IN: std::fmt::Debug,
OUT: Default,
{
Self::from(|input| {
println!("Callback called with: {:?}", input);
OUT::default()
})
}
pub fn then<F, NextOut>(self, next: F) -> Callback<IN, NextOut>
where
F: Fn(OUT) -> NextOut + Send + Sync + 'static,
OUT: 'static,
{
Callback::from(move |input| {
let intermediate = self.emit(input);
next(intermediate)
})
}
pub fn map<F, NewOut>(self, mapper: F) -> Callback<IN, NewOut>
where
F: Fn(OUT) -> NewOut + Send + Sync + 'static,
OUT: 'static,
{
self.then(mapper)
}
pub fn filter<F>(self, predicate: F) -> Callback<IN, Option<OUT>>
where
F: Fn(&IN) -> bool + Send + Sync + 'static,
IN: Clone + 'static,
OUT: 'static,
{
Callback::from(move |input| {
if predicate(&input) {
Some(self.emit(input))
} else {
None
}
})
}
pub fn catch_unwind(self) -> Callback<IN, Result<OUT, String>>
where
IN: 'static,
OUT: 'static,
{
use std::panic::{AssertUnwindSafe, catch_unwind};
Callback::from(move |input| {
catch_unwind(AssertUnwindSafe(|| self.emit(input)))
.map_err(|_| "Callback panicked".to_string())
})
}
}
impl<IN, OUT> Callback<IN, OUT>
where
IN: 'static,
{
pub fn always(value: OUT) -> Self
where
OUT: Clone + Send + Sync + 'static,
{
Self::constant(value)
}
pub fn from_mut<F>(func: F) -> Self
where
F: FnMut(IN) -> OUT + Send + Sync + 'static,
{
use std::sync::Mutex;
let func = Mutex::new(func);
Self::from(move |input| {
let mut guard = func.lock().unwrap();
guard(input)
})
}
}
struct CallbackState<IN, OUT> {
prev_deps: Option<Box<dyn EffectDependencies>>,
callback: Option<Callback<IN, OUT>>,
}
impl<IN, OUT> CallbackState<IN, OUT> {
fn new() -> Self {
Self {
prev_deps: None,
callback: None,
}
}
}
pub fn use_callback<IN, OUT, F, Deps>(func: F, deps: impl Into<Option<Deps>>) -> Callback<IN, OUT>
where
F: Fn(IN) -> OUT + Send + Sync + Clone + 'static,
Deps: EffectDependencies + Clone + PartialEq + 'static,
IN: 'static,
OUT: 'static,
{
let deps = deps.into();
with_hook_context(|ctx| {
let index = ctx.next_hook_index();
let state_ref = ctx.get_or_init_state(index, || CallbackState::<IN, OUT>::new());
let _should_recreate = {
let mut state = state_ref.borrow_mut();
let should_recreate = match &deps {
None => {
state.callback.is_none()
}
Some(current_deps) => {
match &state.prev_deps {
None => {
true
}
Some(prev_deps) => {
!current_deps.deps_eq(prev_deps.as_ref())
}
}
}
};
if should_recreate {
let new_callback = Callback::from(func.clone());
state.callback = Some(new_callback);
if let Some(current_deps) = &deps {
state.prev_deps = Some(current_deps.clone_deps());
} else {
state.prev_deps = None;
}
}
should_recreate
};
let state = state_ref.borrow();
(*state
.callback
.as_ref()
.expect("Callback should be initialized"))
.clone()
})
}