assemble_app/
lib.rs

1use chrono;
2pub use chrono::{DateTime, FixedOffset};
3use serde;
4use serde::de::DeserializeOwned;
5pub use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7pub use wapc_guest::prelude;
8pub use wapc_guest::prelude::{host_call};
9
10#[macro_export]
11macro_rules! register_view {
12    ($n:ident, $t:ident) => {{
13        crate::prelude::register_function(
14            &["view-start-", stringify!($n)].join("")[..],
15            |b: &[u8]| {
16                let (is_connected, params) = crate::deserialize(b)?;
17                let s = $t::start(is_connected, params)?;
18                serialize(&s)
19            },
20        );
21        crate::prelude::register_function(
22            &["view-local-event-", stringify!($n)].join("")[..],
23            |b: &[u8]| {
24                let (state, name, payload): (&[u8], &str, &[u8]) = crate::deserialize(b)?;
25                let mut s: $t = crate::deserialize(state)?;
26                s.local_event(name, payload)?;
27                serialize(&s)
28            },
29        );
30        crate::prelude::register_function(
31            &["view-pubsub-event-", stringify!($n)].join("")[..],
32            |b: &[u8]| {
33                let (state, topic, name, payload): (&[u8], &str, &str, &[u8]) =
34                    crate::deserialize(b)?;
35                let mut s: $t = crate::deserialize(state)?;
36                s.pubsub_event(topic, name, payload)?;
37                serialize(&s)
38            },
39        );
40        crate::prelude::register_function(
41            &["view-presence-event-", stringify!($n)].join("")[..],
42            |b: &[u8]| {
43                let (state, topic, payload): (&[u8], &str, PresenceDiffRaw) =
44                    crate::deserialize(b)?;
45                let mut s: $t = crate::deserialize(state)?;
46                s.presence_event(topic, d)?;
47                serialize(&s)
48            },
49        );
50        crate::prelude::register_function(
51            &["view-render-", stringify!($n)].join("")[..],
52            |b: &[u8]| {
53                let s: $t = crate::deserialize(b)?;
54                let html = s.render()?;
55                Ok(html.into_bytes())
56            },
57        );
58    }};
59}
60#[macro_export]
61macro_rules! register_root_view {
62    ($t:ident) => {{
63        crate::prelude::register_function(&"view-start-", |b: &[u8]| {
64            let (is_connected, params) = crate::deserialize(b)?;
65            let s = $t::start(is_connected, params)?;
66            serialize(&s)
67        });
68        crate::prelude::register_function(&"view-local-event-", |b: &[u8]| {
69            let (state, name, payload): (&[u8], &str, &[u8]) = crate::deserialize(b)?;
70            let mut s: $t = crate::deserialize(state)?;
71            s.local_event(name, payload)?;
72            serialize(&s)
73        });
74        crate::prelude::register_function(&"view-pubsub-event-", |b: &[u8]| {
75            let (state, topic, name, payload): (&[u8], &str, &str, &[u8]) = crate::deserialize(b)?;
76            let mut s: $t = crate::deserialize(state)?;
77            s.pubsub_event(topic, name, payload)?;
78            serialize(&s)
79        });
80        crate::prelude::register_function(&"view-presence-event-", |b: &[u8]| {
81            let (state, topic, payload): (&[u8], &str, PresenceDiffRaw) = crate::deserialize(b)?;
82            let mut s: $t = crate::deserialize(state)?;
83            s.presence_event(topic, payload)?;
84            serialize(&s)
85        });
86        crate::prelude::register_function(&"view-render-", |b: &[u8]| {
87            let s: $t = crate::deserialize(b)?;
88            let html = s.render()?;
89            Ok(html.into_bytes())
90        });
91    }};
92}
93#[macro_export]
94macro_rules! register_call {
95    ($n:ident, $f:ident) => {{
96        crate::prelude::register_function(&["call-", stringify!($n)].join("")[..], |b: &[u8]| {
97            run_function(b, $f)
98        });
99    }};
100}
101#[macro_export]
102macro_rules! assemble_init {
103    ($f:block) => {
104        #[no_mangle]
105        pub extern "C" fn wapc_init() {
106            std::panic::set_hook(Box::new(hook));
107            $f
108        }
109    };
110}
111
112pub fn hook(info: &std::panic::PanicInfo) {
113    let msg = info.to_string();
114    crate::console_log(&msg[..]);
115}
116
117pub type Html = String;
118pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Sync + Send>>;
119
120pub trait View: Sync + Send {
121    fn start(is_connected: bool, params: HashMap<String, String>) -> Result<Self>
122    where
123        Self: Sized;
124    fn local_event(&mut self, _msg: &str, _body: &[u8]) -> Result<()> {
125        Ok(())
126    }
127    fn pubsub_event(&mut self, _topic: &str, _msg: &str, _body: &[u8]) -> Result<()> {
128        Ok(())
129    }
130    fn presence_event(&mut self, _topic: &str, _diff: PresenceDiffRaw) -> Result<()> {
131        Ok(())
132    }
133    fn render(&self) -> Result<Html>;
134}
135
136pub fn serialize<T: ?Sized>(val: &T) -> Result<Vec<u8>>
137where
138    T: Serialize,
139{
140    match rmp_serde::to_vec_named(val) {
141        Ok(v) => Ok(v),
142        Err(v) => Err(Box::new(v)),
143    }
144}
145
146pub fn deserialize<'a, R: ?Sized, T>(rd: &'a R) -> Result<T>
147where
148    R: AsRef<[u8]>,
149    T: Deserialize<'a>,
150{
151    match rmp_serde::from_read_ref(rd) {
152        Ok(v) => Ok(v),
153        Err(v) => Err(Box::new(v)),
154    }
155}
156
157pub fn run_function<T, R>(
158    b: &[u8],
159    f: fn(Option<T>) -> std::result::Result<Box<R>, Box<dyn std::error::Error + Sync + Send>>,
160) -> wapc_guest::prelude::CallResult
161where
162    T: DeserializeOwned,
163    R: Serialize + ?Sized,
164{
165    if b.len() == 0 {
166        match rmp_serde::to_vec(&f(None)?) {
167            Ok(v) => Ok(v),
168            Err(v) => Err(Box::new(v)),
169        }
170    } else {
171        match rmp_serde::from_read_ref(&b) {
172            Ok(input) => match rmp_serde::to_vec(&f(Some(input))?) {
173                Ok(v) => Ok(v),
174                Err(v) => Err(Box::new(v)),
175            },
176            Err(v) => Err(Box::new(v)),
177        }
178    }
179}
180
181#[derive(Deserialize, Serialize, Clone)]
182pub struct User {
183    pub id: String,
184    pub username: String,
185    pub country: String,
186    pub avatar: Option<String>,
187    pub is_anonymous: bool,
188}
189
190#[derive(Deserialize, Serialize, Clone)]
191pub struct ScanOpts {
192    pub limit: u32,
193    pub reverse: bool,
194    pub start_key: Option<String>,
195}
196
197#[derive(Deserialize, Serialize, Clone)]
198pub struct PresenceDiffRaw {
199    pub joins: HashMap<String, Vec<serde_bytes::ByteBuf>>,
200    pub leaves: HashMap<String, Vec<serde_bytes::ByteBuf>>,
201}
202
203#[derive(Deserialize, Serialize)]
204pub struct PresenceDiff<T> {
205    pub joins: HashMap<String, Vec<T>>,
206    pub leaves: HashMap<String, Vec<T>>,
207}
208
209
210impl Default for ScanOpts {
211    fn default() -> ScanOpts {
212        ScanOpts {
213            limit: 100,
214            reverse: false,
215            start_key: None,
216        }
217    }
218}
219
220impl ScanOpts {
221    pub fn limit(&self, limit: u32) -> ScanOpts {
222        let mut m = self.clone();
223        m.limit = limit;
224        m
225    }
226    pub fn reverse(&self, reverse: bool) -> ScanOpts {
227        let mut m = self.clone();
228        m.reverse = reverse;
229        m
230    }
231    pub fn start_key(&self, start_key: String) -> ScanOpts {
232        let mut m = self.clone();
233        m.start_key = Some(start_key);
234        m
235    }
236}
237
238pub fn utc_now() -> Result<DateTime<FixedOffset>> {
239    let res = host_call("v1", "time", "UTC_NOW", &serialize(&())?)?;
240    let st: &str = deserialize(&res)?;
241    match DateTime::parse_from_rfc3339(st) {
242        Ok(v) => Ok(v),
243        Err(e) => Err(e.into()),
244    }
245}
246
247pub fn console_log(s: &str) -> Result<()> {
248    let res = host_call("v1", "console", "LOG", &serialize(&(s,))?)?;
249    deserialize(&res)
250}
251
252pub fn random_string(length: u16) -> Result<String> {
253    let res = host_call("v1", "rand", "STRING", &serialize(&(length,))?)?;
254    deserialize(&res)
255}
256
257pub fn current_user() -> Result<User> {
258    let res = host_call("v1", "user", "USER", &serialize(&())?)?;
259    deserialize(&res)
260}
261
262pub fn counter_get(b: &str, k: &str) -> Result<i64>
263{
264    let res = host_call("v1", "counter", "GET", &serialize(&(b, k))?)?;
265    deserialize(&res)
266}
267
268pub fn counter_increment<T>(b: &str, k: &str, v: i64) -> Result<()>
269{
270    let res = host_call("v1", "counter", "INC", &serialize(&(b, k, v))?)?;
271    deserialize(&res)
272}
273
274pub fn kv_scan<T>(b: &str, opts: &ScanOpts) -> Result<Vec<(String, T)>>
275where
276    T: DeserializeOwned,
277{
278    let res = host_call("v1", "kv", "SCAN", &serialize(&(b, opts))?)?;
279    deserialize(&res)
280}
281
282pub fn kv_set<T>(b: &str, k: &str, obj: &T) -> Result<()>
283where
284    T: Serialize,
285{
286    host_call("v1", "kv", "SET", &serialize(&(b, k, obj))?)?;
287    Ok(())
288}
289
290pub fn kv_patch<T>(b: &str, k: &str, obj: &T) -> Result<()>
291where
292    T: Serialize,
293{
294    host_call("v1", "kv", "PATCH", &serialize(&(b, k, obj))?)?;
295    Ok(())
296}
297
298pub fn kv_delete(b: &str, k: &str) -> Result<()> {
299    host_call("v1", "kv", "DELETE", &serialize(&(b, k))?)?;
300    Ok(())
301}
302
303pub fn kv_patch_delete_fields(b: &str, k: &str, v: Vec<String>) -> Result<()> {
304    host_call("v1", "kv", "PATCH_DEL", &serialize(&(b, k, v))?)?;
305    Ok(())
306}
307
308pub fn pubsub_subscribe(k: &str) -> Result<()> {
309    let res = host_call("v1", "pubsub", "SUB", &serialize(k)?)?;
310    deserialize(&res)
311}
312
313pub fn pubsub_unsubscribe(k: &str) -> Result<()> {
314    let res = host_call("v1", "pubsub", "UNSUB", &serialize(&(k,))?)?;
315    deserialize(&res)
316}
317
318pub fn pubsub_publish<T>(k: &str, event: &str, v: &T) -> Result<()>
319where
320    T: Serialize,
321{
322    let s: serde_bytes::ByteBuf = serde_bytes::ByteBuf::from(serialize(v)?);
323    let res = host_call("v1", "pubsub", "PUB", &serialize(&(k, event, s))?)?;
324    deserialize(&res)
325}
326
327pub fn pubsub_publish_from<T>(k: &str, event: &str, v: &T) -> Result<()>
328where
329    T: Serialize,
330{
331    let s: serde_bytes::ByteBuf = serde_bytes::ByteBuf::from(serialize(v)?);
332    let res = host_call("v1", "pubsub", "PUB_FROM", &serialize(&(k, event, s))?)?;
333    deserialize(&res)
334}
335
336pub fn presence_track<T>(topic: &str, key: &str, v: &T) -> Result<()>
337where
338    T: Serialize,
339{
340    let s: serde_bytes::ByteBuf = serde_bytes::ByteBuf::from(serialize(v)?);
341    let res = host_call("v1", "presence", "TRACK", &serialize(&(topic, key, s))?)?;
342    deserialize(&res)
343}
344
345pub fn presence_list<T>(topic: &str) -> Result<HashMap<String, Vec<T>>>
346where
347    T: DeserializeOwned,
348{
349    let res = host_call("v1", "presence", "LIST", &serialize(&(topic,))?[..])?;
350    
351    let map = deserialize::<_, HashMap<String, Vec<serde_bytes::ByteBuf>>>(&res)?
352        .into_iter()
353        .map(|(k, x)| (k, x.into_iter().flat_map(|e| deserialize(&e)).collect()))
354        .collect();
355
356    Ok(map)
357}
358pub fn presence_deserialize_diff<T>(diff: PresenceDiffRaw) -> Result<PresenceDiff<T>>
359where
360    T: DeserializeOwned,
361{
362    let joins =
363        diff.joins
364        .into_iter()
365        .map(|(k, x)| (k, x.into_iter().flat_map(|e| deserialize(&e)).collect()))
366        .collect();
367    let leaves =
368        diff.leaves
369        .into_iter()
370        .map(|(k, x)| (k, x.into_iter().flat_map(|e| deserialize(&e)).collect()))
371        .collect();
372
373    Ok(PresenceDiff{joins: joins, leaves: leaves})
374}
375pub fn presence_subscribe(k: &str) -> Result<()> {
376    let res = host_call("v1", "presence", "SUB", &serialize(&(k,))?)?;
377    deserialize(&res)
378}
379
380pub fn presence_unsubscribe(k: &str) -> Result<()> {
381    let res = host_call("v1", "presence", "UNSUB", &serialize(&(k,))?)?;
382    deserialize(&res)
383}
384pub fn local_send<T>(event: &str, v: &T) -> Result<()>
385where
386    T: Serialize,
387{
388    local_send_timeout(event, v, 0)
389}
390
391pub fn local_send_timeout<T>(event: &str, v: &T, timeout_ms: u32) -> Result<()>
392where
393    T: Serialize,
394{
395    let res = host_call("v1", "local", "SEND", &serialize(&(event, v, timeout_ms))?)?;
396    deserialize(&res)
397}
398