1pub use webtau_macros::command;
3
4#[macro_export]
31macro_rules! wasm_state {
32 ($T:ty) => {
33 ::std::thread_local! {
34 static __WEBTAU_STATE: ::std::cell::RefCell<Option<$T>> =
35 ::std::cell::RefCell::new(None);
36 }
37
38 fn set_state(val: $T) {
40 __WEBTAU_STATE.with(|cell| {
41 *cell.borrow_mut() = Some(val);
42 });
43 }
44
45 fn with_state<__F, __R>(f: __F) -> __R
50 where
51 __F: FnOnce(&$T) -> __R,
52 {
53 __WEBTAU_STATE.with(|cell| {
54 let borrow = cell.borrow();
55 let state = borrow
56 .as_ref()
57 .expect("webtau: state not initialized — call set_state() first");
58 f(state)
59 })
60 }
61
62 fn with_state_mut<__F, __R>(f: __F) -> __R
67 where
68 __F: FnOnce(&mut $T) -> __R,
69 {
70 __WEBTAU_STATE.with(|cell| {
71 let mut borrow = cell.borrow_mut();
72 let state = borrow
73 .as_mut()
74 .expect("webtau: state not initialized — call set_state() first");
75 f(state)
76 })
77 }
78
79 fn try_with_state<__F, __R>(f: __F) -> Option<__R>
81 where
82 __F: FnOnce(&$T) -> __R,
83 {
84 __WEBTAU_STATE.with(|cell| {
85 let borrow = cell.borrow();
86 borrow.as_ref().map(f)
87 })
88 }
89
90 fn try_with_state_mut<__F, __R>(f: __F) -> Option<__R>
92 where
93 __F: FnOnce(&mut $T) -> __R,
94 {
95 __WEBTAU_STATE.with(|cell| {
96 let mut borrow = cell.borrow_mut();
97 borrow.as_mut().map(f)
98 })
99 }
100 };
101}
102
103#[cfg(test)]
104mod tests {
105 #[derive(Debug, PartialEq)]
106 struct Counter {
107 value: i32,
108 }
109
110 wasm_state!(Counter);
111
112 #[test]
113 fn set_and_read_state() {
114 set_state(Counter { value: 42 });
115 let result = with_state(|c| c.value);
116 assert_eq!(result, 42);
117 }
118
119 #[test]
120 fn mutate_state() {
121 set_state(Counter { value: 0 });
122 with_state_mut(|c| c.value += 10);
123 let result = with_state(|c| c.value);
124 assert_eq!(result, 10);
125 }
126
127 #[test]
128 #[should_panic(expected = "state not initialized")]
129 fn panics_without_init() {
130 struct Uninitialized;
133 wasm_state!(Uninitialized);
134 with_state(|_| {});
135 }
136
137 #[test]
138 fn try_with_state_returns_none_before_init() {
139 struct Sentinel;
140 wasm_state!(Sentinel);
141 let result = try_with_state(|_| 99u32);
142 assert_eq!(result, None);
143 }
144
145 #[test]
146 fn try_with_state_returns_some_after_init() {
147 struct Value {
148 n: i32,
149 }
150 wasm_state!(Value);
151 set_state(Value { n: 7 });
152 let result = try_with_state(|v| v.n);
153 assert_eq!(result, Some(7));
154 }
155
156 #[test]
157 fn try_with_state_mut_returns_none_before_init() {
158 struct Tracker;
159 wasm_state!(Tracker);
160 let result = try_with_state_mut(|_| 42u32);
161 assert_eq!(result, None);
162 }
163}