wavefunk-ui 0.1.3

Askama and htmx UI component base for Wave Funk Rust applications.
Documentation
use serde::Serialize;
use serde_json::{Map, Value};

pub const HX_TRIGGER_HEADER: &str = "HX-Trigger";

#[derive(Clone, Debug, Serialize)]
pub struct MessageEvent<'a> {
    pub kind: &'a str,
    pub msg: &'a str,
}

#[derive(Clone, Debug)]
pub enum Trigger<'a> {
    Toast(MessageEvent<'a>),
    Echo(MessageEvent<'a>),
    Custom { name: &'a str, value: Value },
}

impl<'a> Trigger<'a> {
    pub fn toast(kind: &'a str, msg: &'a str) -> Self {
        Self::Toast(MessageEvent { kind, msg })
    }

    pub fn echo(kind: &'a str, msg: &'a str) -> Self {
        Self::Echo(MessageEvent { kind, msg })
    }

    pub fn custom(name: &'a str, value: Value) -> Self {
        Self::Custom { name, value }
    }
}

pub fn trigger_header(triggers: &[Trigger<'_>]) -> serde_json::Result<String> {
    let mut map = Map::new();

    for trigger in triggers {
        match trigger {
            Trigger::Toast(event) => {
                map.insert("wfToast".to_owned(), serde_json::to_value(event)?);
            }
            Trigger::Echo(event) => {
                map.insert("wfEcho".to_owned(), serde_json::to_value(event)?);
            }
            Trigger::Custom { name, value } => {
                map.insert((*name).to_owned(), value.clone());
            }
        }
    }

    Ok(Value::Object(map).to_string())
}

pub fn trigger_header_pair(triggers: &[Trigger<'_>]) -> serde_json::Result<(&'static str, String)> {
    trigger_header(triggers).map(|value| (HX_TRIGGER_HEADER, value))
}

pub fn toast_header(kind: &str, msg: &str) -> String {
    trigger_header(&[Trigger::toast(kind, msg)])
        .expect("serializing a Wave Funk toast trigger should not fail")
}

pub fn echo_header(kind: &str, msg: &str) -> String {
    trigger_header(&[Trigger::echo(kind, msg)])
        .expect("serializing a Wave Funk echo trigger should not fail")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn builds_hx_trigger_payload() {
        let header = trigger_header(&[Trigger::toast("ok", "Saved.")]).unwrap();
        assert_eq!(header, r#"{"wfToast":{"kind":"ok","msg":"Saved."}}"#);
    }

    #[test]
    fn builds_hx_trigger_response_header_pair() {
        let header = trigger_header_pair(&[
            Trigger::toast("ok", "Saved."),
            Trigger::echo("info", "Queued."),
        ])
        .unwrap();

        assert_eq!(header.0, HX_TRIGGER_HEADER);
        let value: Value = serde_json::from_str(&header.1).unwrap();
        assert_eq!(value["wfToast"]["kind"], "ok");
        assert_eq!(value["wfToast"]["msg"], "Saved.");
        assert_eq!(value["wfEcho"]["kind"], "info");
        assert_eq!(value["wfEcho"]["msg"], "Queued.");
    }
}