[Feat] Split Daemon & TUI
This commit is contained in:
parent
f82500ea7d
commit
36a70e82a0
35 changed files with 970 additions and 239 deletions
5
iota-ipc/src/lib.rs
Normal file
5
iota-ipc/src/lib.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub mod protocol;
|
||||
pub mod transport;
|
||||
|
||||
pub use protocol::{ClientMessage, DaemonMessage, LogEntry, StateSnapshot};
|
||||
pub use transport::{read_msg, write_msg};
|
||||
42
iota-ipc/src/protocol.rs
Normal file
42
iota-ipc/src/protocol.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum ClientMessage {
|
||||
Command { seq: u64, line: String },
|
||||
Subscribe,
|
||||
Ping { seq: u64 },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum DaemonMessage {
|
||||
LogEntry(LogEntry),
|
||||
StateUpdate(StateSnapshot),
|
||||
CommandResult {
|
||||
seq: u64,
|
||||
success: bool,
|
||||
message: String,
|
||||
},
|
||||
Pong {
|
||||
seq: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct LogEntry {
|
||||
pub timestamp_ms: u128,
|
||||
pub sender: String,
|
||||
pub message: String,
|
||||
pub is_error: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct StateSnapshot {
|
||||
pub cpu: Vec<(f64, f64)>,
|
||||
pub ram: Vec<(f64, f64)>,
|
||||
pub ping: Vec<(f64, f64)>,
|
||||
pub net_up: Vec<(f64, f64)>,
|
||||
pub net_down: Vec<(f64, f64)>,
|
||||
pub sys_info: String,
|
||||
}
|
||||
59
iota-ipc/src/transport.rs
Normal file
59
iota-ipc/src/transport.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
|
||||
const MAX_MESSAGE_SIZE: usize = 1024 * 1024;
|
||||
|
||||
/* Length-prefixing preserves message boundaries on a byte stream and bounds
|
||||
* allocations before JSON is deserialized. */
|
||||
pub async fn write_msg<W, T>(writer: &mut W, message: &T) -> Result<()>
|
||||
where
|
||||
W: AsyncWrite + Unpin,
|
||||
T: Serialize,
|
||||
{
|
||||
let payload =
|
||||
serde_json::to_vec(message).map_err(|error| Error::new(ErrorKind::InvalidData, error))?;
|
||||
let len = u32::try_from(payload.len())
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidData, "IPC message is too large"))?;
|
||||
writer.write_u32(len).await?;
|
||||
writer.write_all(&payload).await?;
|
||||
writer.flush().await
|
||||
}
|
||||
|
||||
pub async fn read_msg<R, T>(reader: &mut R) -> Result<T>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let len = reader.read_u32().await? as usize;
|
||||
if len > MAX_MESSAGE_SIZE {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"IPC message exceeds limit",
|
||||
));
|
||||
}
|
||||
let mut payload = vec![0; len];
|
||||
reader.read_exact(&mut payload).await?;
|
||||
serde_json::from_slice(&payload).map_err(|error| Error::new(ErrorKind::InvalidData, error))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{read_msg, write_msg};
|
||||
use crate::protocol::ClientMessage;
|
||||
|
||||
#[tokio::test]
|
||||
async fn round_trips_framed_messages() {
|
||||
let (mut writer, mut reader) = tokio::io::duplex(1024);
|
||||
let message = ClientMessage::Command {
|
||||
seq: 4,
|
||||
line: "help".into(),
|
||||
};
|
||||
write_msg(&mut writer, &message)
|
||||
.await
|
||||
.expect("write succeeds");
|
||||
let received: ClientMessage = read_msg(&mut reader).await.expect("read succeeds");
|
||||
assert!(matches!(received, ClientMessage::Command { seq: 4, line } if line == "help"));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue