Skip to main content

topcoat_runtime/surrogate/
signal.rs

1use ref_cast::RefCast;
2use serde::Serialize;
3
4use crate::{
5    Signal, StrSurrogate, Surrogated, impl_surrogate, impl_surrogate_mut, impl_surrogate_ref,
6};
7
8#[derive(RefCast)]
9#[repr(transparent)]
10pub struct SignalSurrogate<T>(Signal<T>);
11
12impl<T> SignalSurrogate<T> {
13    #[inline]
14    pub(crate) const fn new(v: Signal<T>) -> Self {
15        Self(v)
16    }
17}
18
19impl<T> SignalSurrogate<T>
20where
21    for<'b> &'b T: Surrogated,
22{
23    pub fn read(&self) -> <&T as Surrogated>::Surrogate {
24        self.0.read().into_surrogate()
25    }
26}
27
28impl<T> SignalSurrogate<T>
29where
30    T: Surrogated + Clone,
31{
32    pub fn get(&self) -> <T as Surrogated>::Surrogate {
33        self.0.get().into_surrogate()
34    }
35}
36
37impl<T> SignalSurrogate<T>
38where
39    T: Surrogated,
40{
41    /// Writes a new value to the signal.
42    ///
43    /// # Panics
44    ///
45    /// Always panics; signal writes can only occur in client-side expressions.
46    pub fn set(&self, _v: T::Surrogate) {
47        write_in_browser_only();
48    }
49}
50
51impl SignalSurrogate<bool> {
52    /// Replaces the value with its negation.
53    ///
54    /// # Panics
55    ///
56    /// Always panics; signal writes can only occur in client-side expressions.
57    pub fn toggle(&self) {
58        write_in_browser_only();
59    }
60}
61
62impl SignalSurrogate<f64> {
63    /// Adds one to the value.
64    ///
65    /// # Panics
66    ///
67    /// Always panics; signal writes can only occur in client-side expressions.
68    pub fn increment(&self) {
69        write_in_browser_only();
70    }
71
72    /// Subtracts one from the value.
73    ///
74    /// # Panics
75    ///
76    /// Always panics; signal writes can only occur in client-side expressions.
77    pub fn decrement(&self) {
78        write_in_browser_only();
79    }
80}
81
82impl SignalSurrogate<String> {
83    /// Appends a string to the end of the value.
84    ///
85    /// # Panics
86    ///
87    /// Always panics; signal writes can only occur in client-side expressions.
88    pub fn push_str(&self, _s: &StrSurrogate) {
89        write_in_browser_only();
90    }
91}
92
93/// The panic shared by every signal write evaluated on the server.
94fn write_in_browser_only() -> ! {
95    panic!("expressions in which a signal is written to cannot be run server-side");
96}
97
98impl_surrogate!({T} Signal<T>, SignalSurrogate<T>);
99impl_surrogate_ref!({T} Signal<T>, SignalSurrogate<T>);
100impl_surrogate_mut!({T} Signal<T>, SignalSurrogate<T>);
101
102impl<T> Serialize for SignalSurrogate<T> {
103    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104    where
105        S: serde::Serializer,
106    {
107        #[derive(Serialize)]
108        struct TaggedSignal {
109            t: &'static str,
110            id: std::string::String,
111        }
112
113        TaggedSignal {
114            t: "Signal",
115            id: self.0.id().to_string(),
116        }
117        .serialize(serializer)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    #[should_panic(expected = "cannot be run server-side")]
127    fn toggle_panics_server_side() {
128        SignalSurrogate::new(Signal::new(false)).toggle();
129    }
130
131    #[test]
132    #[should_panic(expected = "cannot be run server-side")]
133    fn increment_panics_server_side() {
134        SignalSurrogate::new(Signal::new(0.0)).increment();
135    }
136
137    #[test]
138    #[should_panic(expected = "cannot be run server-side")]
139    fn decrement_panics_server_side() {
140        SignalSurrogate::new(Signal::new(0.0)).decrement();
141    }
142
143    #[test]
144    #[should_panic(expected = "cannot be run server-side")]
145    fn push_str_panics_server_side() {
146        SignalSurrogate::new(Signal::new(String::new())).push_str(StrSurrogate::ref_cast(""));
147    }
148}