unifier-cli 0.5.0

Filesystem postbox for inter-process communication via a Unix tree
Documentation
//! Mailbox envelope: sender, recipient, unique id, and data packet.

use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;

use crate::error::Result;

/// Sender used when `send` does not specify `--from`.
pub const DEFAULT_SENDER: &str = "unifier";

/// Point-to-point mailbox message stored as JSON in `mailbox/<to>/<id>.txt`.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Envelope {
    pub id: Uuid,
    pub from: String,
    pub to: String,
    pub payload: Value,
}

impl Envelope {
    pub fn new(from: impl Into<String>, to: impl Into<String>, payload: Value) -> Self {
        Self {
            id: Uuid::new_v4(),
            from: from.into(),
            to: to.into(),
            payload,
        }
    }

    pub fn with_id(
        id: Uuid,
        from: impl Into<String>,
        to: impl Into<String>,
        payload: Value,
    ) -> Self {
        Self {
            id,
            from: from.into(),
            to: to.into(),
            payload,
        }
    }

    /// Parse JSON when possible; otherwise treat the raw text as a string packet.
    pub fn parse_payload(raw: &str) -> Value {
        serde_json::from_str(raw).unwrap_or_else(|_| Value::String(raw.to_string()))
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    pub fn from_json(line: &str) -> Result<Self> {
        Ok(serde_json::from_str(line.trim())?)
    }
}

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

    #[test]
    fn roundtrip_json_payload() {
        let env = Envelope::new("alice", "bob", serde_json::json!({"hello": "world"}));
        let parsed: Envelope = serde_json::from_str(&env.to_json().unwrap()).unwrap();
        assert_eq!(parsed.from, "alice");
        assert_eq!(parsed.to, "bob");
        assert_eq!(parsed.id, env.id);
        assert_eq!(parsed.payload["hello"], "world");
    }
}