maycoon_core/signal/
mod.rs

1use crate::app::context::AppContext;
2use crate::reference::Ref;
3use crate::signal::fixed::FixedSignal;
4use crate::signal::map::MapSignal;
5use std::sync::Arc;
6
7/// Contains the [FixedSignal] signal.
8pub mod fixed;
9
10/// Contains the [memoized::MemoizedSignal] signal.
11pub mod memoized;
12
13/// Contains the [state::StateSignal] signal.
14pub mod state;
15
16/// Contains the [MapSignal] signal.
17pub mod map;
18
19/// Contains the [eval::EvalSignal] signal.
20pub mod eval;
21
22/// Listener function for [Signal].
23pub type Listener<T> = Box<dyn Fn(Ref<T>)>;
24
25/// [Arc] reference to a [Signal].
26pub type ArcSignal<T> = Arc<dyn Signal<T>>;
27
28/// Base signal trait.
29///
30/// Signals store values of type `T` and notify listeners when they change.
31///
32/// **NOTE:** By default, signals don't have any listeners. To "hook" a signal into the application cycle, call [use_signal].
33///
34/// [use_signal]: AppContext::use_signal
35pub trait Signal<T: 'static>: 'static {
36    /// Get a reference to the current value of the signal.
37    fn get(&self) -> Ref<T>;
38
39    /// Set the value of the signal.
40    ///
41    /// **NOTE:** This does not notify listeners, use [set] instead.
42    fn set_value(&self, value: T);
43
44    /// Add a listener to the signal, which will be called when the signal's value changes.
45    fn listen(&mut self, listener: Listener<T>);
46
47    /// Notify listeners that the signal's value has changed.
48    /// May also be called manually to update listeners.
49    fn notify(&self);
50
51    /// Set the value of the signal and notify listeners.
52    fn set(&self, value: T) {
53        self.set_value(value);
54        self.notify();
55    }
56
57    /// Converts the signal into a [MaybeSignal].
58    fn maybe(self: &Arc<Self>) -> MaybeSignal<T>
59    where
60        Self: Sized,
61    {
62        MaybeSignal::signal(self.clone())
63    }
64
65    /// Converts this signal into a [MaybeSignal] and applies the given mapping function.
66    ///
67    /// See [MaybeSignal::map] for more.
68    fn map<U: 'static>(self: Arc<Self>, map: impl Fn(Ref<T>) -> Ref<U> + 'static) -> MaybeSignal<U>
69    where
70        Self: Sized,
71    {
72        self.maybe().map(map)
73    }
74
75    /// Hooks the signal into the given [AppContext].
76    ///
77    /// Required for the signal to become reactive with the app lifecycle.
78    fn hook(self, context: &AppContext) -> Arc<Self>
79    where
80        Self: Sized,
81    {
82        context.use_signal(self)
83    }
84}
85
86/// A value which may be a signal or a fixed value.
87#[derive(Clone)]
88pub enum MaybeSignal<T> {
89    /// A signal.
90    Signal(ArcSignal<T>),
91    /// A fixed value wrapped inside an [Arc].
92    Value(Arc<T>),
93}
94
95impl<T: 'static> MaybeSignal<T> {
96    /// Wrap a [Signal] inside a [MaybeSignal].
97    pub fn signal(signal: ArcSignal<T>) -> Self {
98        Self::Signal(signal)
99    }
100
101    /// Wrap a value inside a [MaybeSignal].
102    pub fn value(value: T) -> Self {
103        Self::Value(Arc::new(value))
104    }
105
106    /// Get a reference to the current value.
107    ///
108    /// If the value is a signal, the signal's current value is returned,
109    /// otherwise a [Ref::Arc] of the value is returned.
110    pub fn get(&self) -> Ref<T> {
111        match self {
112            MaybeSignal::Signal(signal) => signal.get(),
113            MaybeSignal::Value(value) => Ref::Arc(value.clone()),
114        }
115    }
116
117    /// Converts the [MaybeSignal] into a [ArcSignal].
118    ///
119    /// If the value is a [MaybeSignal::Value], a [FixedSignal] is created.
120    pub fn into_signal(self) -> ArcSignal<T> {
121        match self {
122            MaybeSignal::Signal(signal) => signal,
123            MaybeSignal::Value(value) => {
124                Arc::new(FixedSignal::new(Arc::into_inner(value).unwrap()))
125            },
126        }
127    }
128
129    /// Converts the [MaybeSignal] into an [ArcSignal] if it is a [MaybeSignal::Signal].
130    pub fn as_signal(&self) -> Option<ArcSignal<T>> {
131        match self {
132            MaybeSignal::Signal(signal) => Some(signal.clone()),
133            _ => None,
134        }
135    }
136
137    /// Applies the given mapping function to the signal.
138    ///
139    /// Returns a [MaybeSignal] containing a [MapSignal] which maps the inner value of the signal.
140    pub fn map<U: 'static>(self, map: impl Fn(Ref<T>) -> Ref<U> + 'static) -> MaybeSignal<U> {
141        let signal = self.into_signal();
142
143        MaybeSignal::signal(Arc::new(MapSignal::new(signal, map)))
144    }
145}
146
147impl<T: Default + 'static> Default for MaybeSignal<T> {
148    fn default() -> Self {
149        Self::value(T::default())
150    }
151}
152
153impl<T: 'static> From<T> for MaybeSignal<T> {
154    fn from(value: T) -> Self {
155        Self::value(value)
156    }
157}
158
159impl<T: 'static> From<ArcSignal<T>> for MaybeSignal<T> {
160    fn from(signal: ArcSignal<T>) -> Self {
161        Self::signal(signal)
162    }
163}
164
165impl<'a, T: 'static> From<&'a ArcSignal<T>> for MaybeSignal<T> {
166    fn from(value: &'a ArcSignal<T>) -> Self {
167        Self::signal(value.clone())
168    }
169}