Skip to main content

unifier/daemon/
protocol.rs

1//! Line-delimited JSON protocol between CLI clients and the hot daemon.
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::postbox::Message;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "op", rename_all = "snake_case")]
10pub enum Request {
11    Put { key: String, value: String },
12    Get { key: String },
13    Del { key: String },
14    Send {
15        #[serde(default)]
16        from: Option<String>,
17        recipient: String,
18        message: String,
19    },
20    Cron { schedule: String, message: String },
21    Poll { recipient: String },
22    PollCron,
23    List { path: String },
24    Ack { id_or_path: String },
25    Flush,
26    Ping,
27    Shutdown,
28    TickStart { label: String },
29    TickEnd,
30    TickStatus,
31    TickLock { key: String },
32    TickUnlock { key: String },
33    Event { payload: String },
34    AgentMessage {
35        from: String,
36        to: String,
37        payload: String,
38    },
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(tag = "status", rename_all = "snake_case")]
43pub enum Response {
44    Ok {
45        #[serde(default, skip_serializing_if = "Option::is_none")]
46        value: Option<String>,
47        #[serde(default, skip_serializing_if = "Option::is_none")]
48        uuid: Option<Uuid>,
49        #[serde(default, skip_serializing_if = "Option::is_none")]
50        found: Option<bool>,
51        #[serde(default, skip_serializing_if = "Option::is_none")]
52        dirty: Option<bool>,
53        #[serde(default, skip_serializing_if = "Option::is_none")]
54        tick: Option<u64>,
55        #[serde(default, skip_serializing_if = "Option::is_none")]
56        queued: Option<usize>,
57        #[serde(default, skip_serializing_if = "Vec::is_empty")]
58        messages: Vec<MessageDto>,
59    },
60    Err { error: String },
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct MessageDto {
65    pub id: Uuid,
66    pub path: String,
67    pub body: String,
68}
69
70impl From<Message> for MessageDto {
71    fn from(m: Message) -> Self {
72        Self {
73            id: m.id,
74            path: m.path.display().to_string(),
75            body: m.body,
76        }
77    }
78}
79
80impl From<MessageDto> for Message {
81    fn from(m: MessageDto) -> Self {
82        Self {
83            id: m.id,
84            path: m.path.into(),
85            body: m.body,
86        }
87    }
88}
89
90pub fn encode_response(response: &Response) -> Result<String, serde_json::Error> {
91    Ok(format!("{}\n", serde_json::to_string(response)?))
92}
93
94pub fn decode_request(line: &str) -> Result<Request, serde_json::Error> {
95    serde_json::from_str(line.trim())
96}
97
98pub fn decode_response(line: &str) -> Result<Response, serde_json::Error> {
99    serde_json::from_str(line.trim())
100}
101
102pub fn ok_empty() -> Response {
103    Response::Ok {
104        value: None,
105        uuid: None,
106        found: None,
107        dirty: None,
108        tick: None,
109        queued: None,
110        messages: vec![],
111    }
112}