nami_core/
lib.rs

1//! Core components for the Nami framework.
2
3#![no_std]
4#![forbid(unsafe_code)]
5extern crate alloc;
6
7use crate::watcher::{Context, WatcherGuard};
8
9/// Collection types for Nami.
10pub mod collection;
11pub mod dictionary;
12pub mod watcher;
13/// The core trait for reactive system.
14///
15/// Types implementing `Signal` represent a computation that can produce a value
16/// and notify observers when that value changes.
17pub trait Signal: Clone + 'static {
18    /// The type of value produced by this computation.
19    type Output: 'static;
20    /// The guard type returned by the watch method that manages watcher lifecycle.
21    type Guard: WatcherGuard;
22
23    /// Execute the computation and return the current value.
24    fn get(&self) -> Self::Output;
25
26    /// Register a watcher to be notified when the computed value changes.
27    ///
28    /// Returns a guard that, when dropped, will unregister the watcher.
29    #[must_use]
30    fn watch(&self, watcher: impl Fn(Context<Self::Output>) + 'static) -> Self::Guard;
31}
32
33/// The `CustomBinding` trait represents a computable value that can also be set.
34///
35/// Any type implementing this trait must also implement `Signal` to provide the
36/// ability to retrieve its current value, and adds the ability to mutate the value.
37pub trait CustomBinding: Signal {
38    /// Sets a new value for this binding.
39    ///
40    /// This will typically trigger notifications to any watchers.
41    fn set(&self, value: Self::Output);
42}
43
44/// Macro to implement the Signal trait for constant types.
45///
46/// This macro generates Signal implementations for types that don't change,
47/// providing them with empty watcher functionality since they never notify changes.
48#[macro_export]
49macro_rules! impl_constant {
50    ($($ty:ty),*) => {
51         $(
52            impl $crate::Signal for $ty {
53                type Output = Self;
54                type Guard = ();
55
56                fn get(&self) -> Self::Output {
57                    self.clone()
58                }
59
60                fn watch(
61                    &self,
62                    _watcher: impl Fn($crate::watcher::Context<Self::Output>)+'static,
63                )  {
64
65                }
66            }
67        )*
68    };
69
70}
71
72macro_rules! impl_generic_constant {
73
74    ( $($ty:ident < $($param:ident),* >),* $(,)? ) => {
75        $(
76            impl<$($param: Clone + 'static),*> $crate::Signal for $ty<$($param),*> {
77                type Output = Self;
78                type Guard = ();
79
80                fn get(&self) -> Self::Output {
81                    self.clone()
82                }
83
84                fn watch(
85                    &self,
86                    _watcher: impl Fn($crate::watcher::Context<Self::Output>)+'static,
87                ) {
88
89                }
90            }
91        )*
92    };
93
94
95
96
97}
98
99mod impl_constant {
100    use alloc::borrow::Cow;
101    use alloc::collections::BTreeMap;
102    use core::time::Duration;
103
104    use crate::Signal;
105    use alloc::string::String;
106    use alloc::vec::Vec;
107    impl_constant!(
108        &'static str,
109        u8,
110        u16,
111        u32,
112        u64,
113        i8,
114        i16,
115        i32,
116        i64,
117        f32,
118        f64,
119        bool,
120        char,
121        Duration,
122        String,
123        Cow<'static, str>
124    );
125
126    impl_generic_constant!(Vec<T>,BTreeMap<K,V>);
127
128    impl<T: 'static> Signal for &'static [T] {
129        type Output = &'static [T];
130        type Guard = ();
131        fn get(&self) -> Self::Output {
132            self
133        }
134        fn watch(&self, _watcher: impl Fn(crate::watcher::Context<Self::Output>) + 'static) {}
135    }
136}
137
138impl<T: Signal> Signal for Option<T> {
139    type Output = Option<T::Output>;
140    type Guard = Option<T::Guard>;
141    fn get(&self) -> Self::Output {
142        self.as_ref().map(Signal::get)
143    }
144    fn watch(&self, watcher: impl Fn(Context<Self::Output>) + 'static) -> Self::Guard {
145        self.as_ref()
146            .map(|s| s.watch(move |context| watcher(context.map(Some))))
147    }
148}
149
150impl<T: Signal, E: Signal> Signal for Result<T, E> {
151    type Output = Result<T::Output, E::Output>;
152    type Guard = Result<T::Guard, E::Guard>;
153    fn get(&self) -> Self::Output {
154        match &self {
155            Ok(s) => Ok(s.get()),
156            Err(e) => Err(e.get()),
157        }
158    }
159    fn watch(&self, watcher: impl Fn(Context<Self::Output>) + 'static) -> Self::Guard {
160        match &self {
161            Ok(s) => Ok(s.watch(move |context| watcher(context.map(Ok)))),
162            Err(e) => Err(e.watch(move |context| watcher(context.map(Err)))),
163        }
164    }
165}