Skip to main content

syspulse_core/ipc/
protocol.rs

1use serde::{Deserialize, Serialize};
2use tokio::io::{AsyncReadExt, AsyncWriteExt};
3
4use crate::daemon::{DaemonInstance, DaemonSpec};
5use crate::error::SyspulseError;
6
7#[derive(Debug, Serialize, Deserialize)]
8#[serde(tag = "type", rename_all = "snake_case")]
9pub enum Request {
10    Start {
11        name: String,
12        wait: bool,
13        timeout_secs: Option<u64>,
14    },
15    Stop {
16        name: String,
17        force: bool,
18        timeout_secs: Option<u64>,
19    },
20    Restart {
21        name: String,
22        force: bool,
23        wait: bool,
24    },
25    Status {
26        name: Option<String>,
27    },
28    List,
29    Logs {
30        name: String,
31        lines: usize,
32        stderr: bool,
33    },
34    Add {
35        spec: DaemonSpec,
36    },
37    Remove {
38        name: String,
39        force: bool,
40    },
41    Shutdown,
42    Ping,
43}
44
45#[derive(Debug, Serialize, Deserialize)]
46#[serde(tag = "type", rename_all = "snake_case")]
47pub enum Response {
48    Ok { message: String },
49    Status { instance: DaemonInstance },
50    List { instances: Vec<DaemonInstance> },
51    Logs { lines: Vec<String> },
52    Pong,
53    Error { code: u32, message: String },
54}
55
56/// Encode a message as 4-byte big-endian length prefix + JSON payload.
57pub fn encode_message<T: Serialize>(msg: &T) -> crate::error::Result<Vec<u8>> {
58    let json = serde_json::to_vec(msg)?;
59    let len = (json.len() as u32).to_be_bytes();
60    let mut buf = Vec::with_capacity(4 + json.len());
61    buf.extend_from_slice(&len);
62    buf.extend_from_slice(&json);
63    Ok(buf)
64}
65
66/// Read a length-prefixed JSON message from an async reader.
67/// Returns `Ok(None)` on clean EOF (peer disconnected).
68pub async fn read_message<T: serde::de::DeserializeOwned>(
69    reader: &mut (impl AsyncReadExt + Unpin),
70) -> crate::error::Result<Option<T>> {
71    let mut len_buf = [0u8; 4];
72    match reader.read_exact(&mut len_buf).await {
73        Ok(_) => {}
74        Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
75        Err(e) => return Err(e.into()),
76    }
77    let len = u32::from_be_bytes(len_buf) as usize;
78    if len > 10 * 1024 * 1024 {
79        return Err(SyspulseError::Ipc("Message too large (>10MB)".into()));
80    }
81    let mut payload = vec![0u8; len];
82    reader.read_exact(&mut payload).await?;
83    let msg = serde_json::from_slice(&payload)?;
84    Ok(Some(msg))
85}
86
87/// Write a length-prefixed JSON message to an async writer.
88pub async fn write_message<T: Serialize>(
89    writer: &mut (impl AsyncWriteExt + Unpin),
90    msg: &T,
91) -> crate::error::Result<()> {
92    let encoded = encode_message(msg)?;
93    writer.write_all(&encoded).await?;
94    writer.flush().await?;
95    Ok(())
96}