Skip to main content

wavefunk_ui/
htmx.rs

1use serde::Serialize;
2use serde_json::{Map, Value};
3
4pub const HX_TRIGGER_HEADER: &str = "HX-Trigger";
5
6#[derive(Clone, Debug, Serialize)]
7pub struct MessageEvent<'a> {
8    pub kind: &'a str,
9    pub msg: &'a str,
10}
11
12#[derive(Clone, Debug)]
13pub enum Trigger<'a> {
14    Toast(MessageEvent<'a>),
15    Echo(MessageEvent<'a>),
16    Custom { name: &'a str, value: Value },
17}
18
19impl<'a> Trigger<'a> {
20    pub fn toast(kind: &'a str, msg: &'a str) -> Self {
21        Self::Toast(MessageEvent { kind, msg })
22    }
23
24    pub fn echo(kind: &'a str, msg: &'a str) -> Self {
25        Self::Echo(MessageEvent { kind, msg })
26    }
27
28    pub fn custom(name: &'a str, value: Value) -> Self {
29        Self::Custom { name, value }
30    }
31}
32
33pub fn trigger_header(triggers: &[Trigger<'_>]) -> serde_json::Result<String> {
34    let mut map = Map::new();
35
36    for trigger in triggers {
37        match trigger {
38            Trigger::Toast(event) => {
39                map.insert("wfToast".to_owned(), serde_json::to_value(event)?);
40            }
41            Trigger::Echo(event) => {
42                map.insert("wfEcho".to_owned(), serde_json::to_value(event)?);
43            }
44            Trigger::Custom { name, value } => {
45                map.insert((*name).to_owned(), value.clone());
46            }
47        }
48    }
49
50    Ok(Value::Object(map).to_string())
51}
52
53pub fn trigger_header_pair(triggers: &[Trigger<'_>]) -> serde_json::Result<(&'static str, String)> {
54    trigger_header(triggers).map(|value| (HX_TRIGGER_HEADER, value))
55}
56
57pub fn toast_header(kind: &str, msg: &str) -> String {
58    trigger_header(&[Trigger::toast(kind, msg)])
59        .expect("serializing a Wave Funk toast trigger should not fail")
60}
61
62pub fn echo_header(kind: &str, msg: &str) -> String {
63    trigger_header(&[Trigger::echo(kind, msg)])
64        .expect("serializing a Wave Funk echo trigger should not fail")
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn builds_hx_trigger_payload() {
73        let header = trigger_header(&[Trigger::toast("ok", "Saved.")]).unwrap();
74        assert_eq!(header, r#"{"wfToast":{"kind":"ok","msg":"Saved."}}"#);
75    }
76
77    #[test]
78    fn builds_hx_trigger_response_header_pair() {
79        let header = trigger_header_pair(&[
80            Trigger::toast("ok", "Saved."),
81            Trigger::echo("info", "Queued."),
82        ])
83        .unwrap();
84
85        assert_eq!(header.0, HX_TRIGGER_HEADER);
86        let value: Value = serde_json::from_str(&header.1).unwrap();
87        assert_eq!(value["wfToast"]["kind"], "ok");
88        assert_eq!(value["wfToast"]["msg"], "Saved.");
89        assert_eq!(value["wfEcho"]["kind"], "info");
90        assert_eq!(value["wfEcho"]["msg"], "Queued.");
91    }
92}