topcoat_runtime/surrogate/
signal.rs1use std::ops::Deref;
2
3use ref_cast::RefCast;
4use serde::Serialize;
5
6use crate::{
7 Signal, StrSurrogate, Surrogated, impl_surrogate, impl_surrogate_mut, impl_surrogate_ref,
8};
9
10#[derive(RefCast)]
11#[repr(transparent)]
12pub struct SignalSurrogate<T>(Signal<T>);
13
14impl<T> SignalSurrogate<T> {
15 #[inline]
16 pub(crate) const fn new(v: Signal<T>) -> Self {
17 Self(v)
18 }
19}
20
21impl<T> SignalSurrogate<T>
22where
23 for<'b> &'b T: Surrogated,
24{
25 pub fn read(&self) -> <&T as Surrogated>::Surrogate {
26 self.0.read().into_surrogate()
27 }
28}
29
30impl<T> SignalSurrogate<T>
31where
32 T: Surrogated + Clone,
33{
34 pub fn get(&self) -> <T as Surrogated>::Surrogate {
35 self.0.get().into_surrogate()
36 }
37}
38
39impl<T> SignalSurrogate<T>
40where
41 T: Surrogated,
42{
43 #[track_caller]
49 pub fn set(&self, _v: T::Surrogate) {
50 write_in_browser_only();
51 }
52}
53
54impl SignalSurrogate<bool> {
55 #[track_caller]
61 pub fn toggle(&self) {
62 write_in_browser_only();
63 }
64}
65
66impl SignalSurrogate<f64> {
67 #[track_caller]
73 pub fn increment(&self) {
74 write_in_browser_only();
75 }
76
77 #[track_caller]
83 pub fn decrement(&self) {
84 write_in_browser_only();
85 }
86}
87
88impl SignalSurrogate<String> {
89 #[track_caller]
100 pub fn push_str(&self, _s: impl Deref<Target = StrSurrogate>) {
101 write_in_browser_only();
102 }
103}
104
105#[track_caller]
107fn write_in_browser_only() -> ! {
108 panic!("expressions in which a signal is written to cannot be run server-side");
109}
110
111impl_surrogate!({T} Signal<T>, SignalSurrogate<T>);
112impl_surrogate_ref!({T} Signal<T>, SignalSurrogate<T>);
113impl_surrogate_mut!({T} Signal<T>, SignalSurrogate<T>);
114
115impl<T> Serialize for SignalSurrogate<T> {
116 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
117 where
118 S: serde::Serializer,
119 {
120 #[derive(Serialize)]
121 struct TaggedSignal {
122 t: &'static str,
123 id: std::string::String,
124 }
125
126 TaggedSignal {
127 t: "Signal",
128 id: self.0.id().to_string(),
129 }
130 .serialize(serializer)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 #[should_panic(expected = "cannot be run server-side")]
140 fn toggle_panics_server_side() {
141 SignalSurrogate::new(Signal::new(false)).toggle();
142 }
143
144 #[test]
145 #[should_panic(expected = "cannot be run server-side")]
146 fn increment_panics_server_side() {
147 SignalSurrogate::new(Signal::new(0.0)).increment();
148 }
149
150 #[test]
151 #[should_panic(expected = "cannot be run server-side")]
152 fn decrement_panics_server_side() {
153 SignalSurrogate::new(Signal::new(0.0)).decrement();
154 }
155
156 #[test]
157 #[should_panic(expected = "cannot be run server-side")]
158 fn push_str_panics_server_side() {
159 SignalSurrogate::new(Signal::new(String::new())).push_str(StrSurrogate::ref_cast(""));
160 }
161}