use crate::{
diagnostics,
diagnostics::*,
node::NodeId,
runtime::{with_runtime, Runtime},
SignalGet, SignalSet, SignalUpdate,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Trigger {
pub(crate) id: NodeId,
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static std::panic::Location<'static>,
}
impl Trigger {
#[inline(always)]
#[track_caller]
pub fn new() -> Self {
create_trigger()
}
pub fn notify(&self) {
assert!(self.try_notify(), "Trigger::notify(): runtime not alive")
}
pub fn try_notify(&self) -> bool {
with_runtime(|runtime| {
runtime.mark_dirty(self.id);
runtime.run_effects();
})
.is_ok()
}
pub fn track(&self) {
assert!(self.try_track(), "Trigger::track(): runtime not alive")
}
pub fn try_track(&self) -> bool {
let diagnostics = diagnostics!(self);
with_runtime(|runtime| {
runtime.update_if_necessary(self.id);
self.id.subscribe(runtime, diagnostics);
})
.is_ok()
}
}
#[cfg_attr(debug_assertions, instrument(level = "trace", skip_all,))]
#[track_caller]
pub fn create_trigger() -> Trigger {
Runtime::current().create_trigger()
}
impl Default for Trigger {
fn default() -> Self {
Self::new()
}
}
impl SignalGet for Trigger {
type Value = ();
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
name = "Trigger::get()",
skip_all,
fields(
id = ?self.id,
defined_at = %self.defined_at
)
)
)]
#[track_caller]
#[inline(always)]
fn get(&self) {
self.track()
}
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
name = "Trigger::try_get()",
skip_all,
fields(
id = ?self.id,
defined_at = %self.defined_at
)
)
)]
#[inline(always)]
fn try_get(&self) -> Option<()> {
self.try_track().then_some(())
}
}
impl SignalUpdate for Trigger {
type Value = ();
#[cfg_attr(
debug_assertions,
instrument(
name = "Trigger::update()",
level = "trace",
skip_all,
fields(
id = ?self.id,
defined_at = %self.defined_at
)
)
)]
#[inline(always)]
fn update(&self, f: impl FnOnce(&mut ())) {
self.try_update(f).expect("runtime to be alive")
}
#[cfg_attr(
debug_assertions,
instrument(
name = "Trigger::try_update()",
level = "trace",
skip_all,
fields(
id = ?self.id,
defined_at = %self.defined_at
)
)
)]
#[inline(always)]
fn try_update<O>(&self, f: impl FnOnce(&mut ()) -> O) -> Option<O> {
with_runtime(|runtime| {
let res = f(&mut ());
runtime.mark_dirty(self.id);
runtime.run_effects();
Some(res)
})
.ok()
.flatten()
}
}
impl SignalSet for Trigger {
type Value = ();
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
name = "Trigger::set()",
skip_all,
fields(
id = ?self.id,
defined_at = %self.defined_at
)
)
)]
#[inline(always)]
fn set(&self, _: ()) {
self.notify();
}
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
name = "Trigger::try_set()",
skip_all,
fields(
id = ?self.id,
defined_at = %self.defined_at
)
)
)]
#[inline(always)]
fn try_set(&self, _: ()) -> Option<()> {
self.try_notify().then_some(())
}
}
#[cfg(feature = "nightly")]
impl FnOnce<()> for Trigger {
type Output = ();
#[inline(always)]
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
self.track()
}
}
#[cfg(feature = "nightly")]
impl FnMut<()> for Trigger {
#[inline(always)]
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
self.track()
}
}
#[cfg(feature = "nightly")]
impl Fn<()> for Trigger {
#[inline(always)]
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
self.track()
}
}