unifier-cli 0.3.0

Filesystem postbox for inter-process communication via a Unix tree
Documentation
//! Line-delimited JSON protocol between CLI clients and the hot daemon.

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

use crate::postbox::Message;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "snake_case")]
pub enum Request {
    Put {
        key: String,
        value: String,
    },
    Get {
        key: String,
    },
    Del {
        key: String,
    },
    Send {
        #[serde(default)]
        from: Option<String>,
        recipient: String,
        message: String,
    },
    Cron {
        schedule: String,
        message: String,
    },
    Poll {
        recipient: String,
    },
    PollCron,
    List {
        path: String,
    },
    Ack {
        id_or_path: String,
    },
    Flush,
    Ping,
    Shutdown,
    TickStart {
        label: String,
    },
    TickEnd,
    TickStatus,
    TickLock {
        key: String,
    },
    TickUnlock {
        key: String,
    },
    Event {
        payload: String,
    },
    AgentMessage {
        from: String,
        to: String,
        payload: String,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum Response {
    Ok {
        #[serde(default, skip_serializing_if = "Option::is_none")]
        value: Option<String>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        uuid: Option<Uuid>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        found: Option<bool>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        dirty: Option<bool>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        tick: Option<u64>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        queued: Option<usize>,
        #[serde(default, skip_serializing_if = "Vec::is_empty")]
        messages: Vec<MessageDto>,
    },
    Err {
        error: String,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageDto {
    pub id: Uuid,
    pub path: String,
    pub body: String,
}

impl From<Message> for MessageDto {
    fn from(m: Message) -> Self {
        Self {
            id: m.id,
            path: m.path.display().to_string(),
            body: m.body,
        }
    }
}

impl From<MessageDto> for Message {
    fn from(m: MessageDto) -> Self {
        Self {
            id: m.id,
            path: m.path.into(),
            body: m.body,
        }
    }
}

pub fn encode_response(response: &Response) -> Result<String, serde_json::Error> {
    Ok(format!("{}\n", serde_json::to_string(response)?))
}

pub fn decode_request(line: &str) -> Result<Request, serde_json::Error> {
    serde_json::from_str(line.trim())
}

pub fn decode_response(line: &str) -> Result<Response, serde_json::Error> {
    serde_json::from_str(line.trim())
}

pub fn ok_empty() -> Response {
    Response::Ok {
        value: None,
        uuid: None,
        found: None,
        dirty: None,
        tick: None,
        queued: None,
        messages: vec![],
    }
}