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 {
12        key: String,
13        value: String,
14    },
15    Get {
16        key: String,
17    },
18    Del {
19        key: String,
20    },
21    Send {
22        #[serde(default)]
23        from: Option<String>,
24        recipient: String,
25        message: String,
26    },
27    Cron {
28        schedule: String,
29        message: String,
30    },
31    Poll {
32        recipient: String,
33    },
34    PollCron,
35    List {
36        path: String,
37    },
38    Ack {
39        id_or_path: String,
40    },
41    Flush,
42    Ping,
43    Shutdown,
44    TickStart {
45        label: String,
46    },
47    TickEnd,
48    TickStatus,
49    TickLock {
50        key: String,
51    },
52    TickUnlock {
53        key: String,
54    },
55    Event {
56        payload: String,
57    },
58    AgentMessage {
59        from: String,
60        to: String,
61        payload: String,
62    },
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(tag = "status", rename_all = "snake_case")]
67pub enum Response {
68    Ok {
69        #[serde(default, skip_serializing_if = "Option::is_none")]
70        value: Option<String>,
71        #[serde(default, skip_serializing_if = "Option::is_none")]
72        uuid: Option<Uuid>,
73        #[serde(default, skip_serializing_if = "Option::is_none")]
74        found: Option<bool>,
75        #[serde(default, skip_serializing_if = "Option::is_none")]
76        dirty: Option<bool>,
77        #[serde(default, skip_serializing_if = "Option::is_none")]
78        tick: Option<u64>,
79        #[serde(default, skip_serializing_if = "Option::is_none")]
80        queued: Option<usize>,
81        #[serde(default, skip_serializing_if = "Vec::is_empty")]
82        messages: Vec<MessageDto>,
83    },
84    Err {
85        error: String,
86    },
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct MessageDto {
91    pub id: Uuid,
92    pub path: String,
93    pub body: String,
94}
95
96impl From<Message> for MessageDto {
97    fn from(m: Message) -> Self {
98        Self {
99            id: m.id,
100            path: m.path.display().to_string(),
101            body: m.body,
102        }
103    }
104}
105
106impl From<MessageDto> for Message {
107    fn from(m: MessageDto) -> Self {
108        Self {
109            id: m.id,
110            path: m.path.into(),
111            body: m.body,
112        }
113    }
114}
115
116pub fn encode_response(response: &Response) -> Result<String, serde_json::Error> {
117    Ok(format!("{}\n", serde_json::to_string(response)?))
118}
119
120pub fn decode_request(line: &str) -> Result<Request, serde_json::Error> {
121    serde_json::from_str(line.trim())
122}
123
124pub fn decode_response(line: &str) -> Result<Response, serde_json::Error> {
125    serde_json::from_str(line.trim())
126}
127
128pub fn ok_empty() -> Response {
129    Response::Ok {
130        value: None,
131        uuid: None,
132        found: None,
133        dirty: None,
134        tick: None,
135        queued: None,
136        messages: vec![],
137    }
138}