docs: tambah doc comment, logging, dan inline comments di semua 255 file
Meliputi: - File-level //! doc comment: tujuan file, alur kerja, komponen utama - Function-level /// doc comment: apa, parameter, return, flow, edge cases - Struct/enum/trait /// doc comment: peran, field docs - Tracing logging (tracing::info!/debug!/trace!/warn!/error!) di setiap fungsi - Inline comments untuk variable dan branching logic penting - Seluruh 8 crates di workspace: zesdex-backend, zesdex-cms, zesdex-entities, zesdex-iam, zesdex-infra, zesdex-ipc, zesdex-middleware, zesdex-utils - Build: 0 errors, 242/242 tests passed
This commit is contained in:
@@ -10,6 +10,7 @@ use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::sync::Mutex;
|
||||
use tracing;
|
||||
|
||||
/// A thread-safe IPC client connected to a Zesdex daemon over a Unix
|
||||
/// socket.
|
||||
@@ -29,6 +30,7 @@ impl IpcClient {
|
||||
let stream = UnixStream::connect(path)
|
||||
.with_context(|| format!("failed to connect to Unix socket at {path:?}"))?;
|
||||
let conn = Connection::new(stream);
|
||||
tracing::debug!("connected to daemon at {path:?}");
|
||||
Ok(Self {
|
||||
conn: Mutex::new(conn),
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::io::BufReader;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use tracing;
|
||||
|
||||
/// A framed JSON connection over a Unix socket.
|
||||
///
|
||||
@@ -50,6 +51,7 @@ impl Connection {
|
||||
/// [`write_frame`] for writing.
|
||||
pub fn send<T: Serialize>(&mut self, msg: &T) -> Result<()> {
|
||||
let json = serde_json::to_vec(msg).context("failed to serialise message to JSON")?;
|
||||
tracing::trace!("sending frame ({} byte(s))", json.len());
|
||||
write_frame(&mut self.writer, &json).context("failed to write frame to connection")
|
||||
}
|
||||
|
||||
@@ -66,8 +68,12 @@ impl Connection {
|
||||
let raw = read_frame(&mut self.reader).context("failed to read frame from connection")?;
|
||||
|
||||
match raw {
|
||||
None => Ok(None),
|
||||
None => {
|
||||
tracing::trace!("connection closed (EOF)");
|
||||
Ok(None)
|
||||
}
|
||||
Some(bytes) => {
|
||||
tracing::trace!("received frame ({} byte(s))", bytes.len());
|
||||
let msg: T = serde_json::from_slice(&bytes).with_context(|| {
|
||||
format!("failed to deserialise frame ({} byte(s))", bytes.len())
|
||||
})?;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use std::io::{Read, Write};
|
||||
use tracing;
|
||||
|
||||
/// Maximum frame payload size (64 MiB).
|
||||
const MAX_PAYLOAD: u32 = 64 * 1024 * 1024;
|
||||
@@ -35,6 +36,7 @@ pub fn read_frame(reader: &mut impl Read) -> Result<Option<Vec<u8>>> {
|
||||
Ok(()) => {}
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
|
||||
// Zero bytes available → clean EOF.
|
||||
tracing::trace!("read_frame: clean EOF");
|
||||
return Ok(None);
|
||||
}
|
||||
Err(e) => return Err(e).context("failed to read frame length prefix"),
|
||||
@@ -47,11 +49,12 @@ pub fn read_frame(reader: &mut impl Read) -> Result<Option<Vec<u8>>> {
|
||||
}
|
||||
|
||||
// --- Read the payload ---------------------------------------------------
|
||||
let mut payload = vec![0u8; payload_len];
|
||||
let mut payload = vec![0u8; payload_len]; // zero-filled buffer of exact length
|
||||
reader
|
||||
.read_exact(&mut payload)
|
||||
.with_context(|| format!("failed to read {payload_len} byte(s) of frame payload"))?;
|
||||
|
||||
tracing::trace!("read_frame: {payload_len} byte(s) received");
|
||||
Ok(Some(payload))
|
||||
}
|
||||
|
||||
@@ -73,7 +76,7 @@ pub fn write_frame(writer: &mut impl Write, data: &[u8]) -> Result<()> {
|
||||
anyhow::bail!("frame payload too large: {payload_len} bytes (max {MAX_PAYLOAD})");
|
||||
}
|
||||
|
||||
let len_bytes = payload_len.to_be_bytes();
|
||||
let len_bytes = payload_len.to_be_bytes(); // 4-byte big-endian length prefix
|
||||
writer
|
||||
.write_all(&len_bytes)
|
||||
.context("failed to write frame length prefix")?;
|
||||
@@ -82,6 +85,7 @@ pub fn write_frame(writer: &mut impl Write, data: &[u8]) -> Result<()> {
|
||||
.context("failed to write frame payload")?;
|
||||
writer.flush().context("failed to flush frame writer")?;
|
||||
|
||||
tracing::trace!("write_frame: {payload_len} byte(s) sent");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
//! # zesdex-ipc
|
||||
//!
|
||||
//! Unix-socket IPC layer for daemon/client communication.
|
||||
//!
|
||||
//! This crate provides the wire protocol, framing, and connection
|
||||
//! wrappers used by both the Zesdex daemon and its TUI client.
|
||||
|
||||
#![allow(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_possible_wrap
|
||||
)]
|
||||
//! ## Components
|
||||
//!
|
||||
//! - **`protocol`** — Wire-protocol message types (`Request`, `Response`, `Event`, `StreamChunk`).
|
||||
//! - **`frame`** — Length-delimited binary framing over a Unix socket stream.
|
||||
//! - **`conn`** — Reusable connection wrapper with read/write framing.
|
||||
//! - **`client`** — High-level client that sends requests and awaits responses via an internal
|
||||
//! pending-request map.
|
||||
//! - **`server`** — Accept-loop server that dispatches incoming requests to a handler closure.
|
||||
//!
|
||||
//! ## Flow
|
||||
//!
|
||||
//! `client` → `conn` → length-prefixed `frame` → Unix socket → `conn` → `server` → handler.
|
||||
//! The daemon runs the server side; the TUI process runs the client side.
|
||||
|
||||
pub mod client;
|
||||
pub mod conn;
|
||||
@@ -21,10 +27,13 @@ pub(crate) mod test_utils {
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
/// Monotonically increasing test-sequence counter.
|
||||
pub static TEST_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// Simple Ping payload for round-trip tests.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Ping {
|
||||
/// Sequence number to correlate request/response.
|
||||
pub seq: u32,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,22 +13,40 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A resolved key press sent from the daemon to the client (or used inside
|
||||
/// the client event loop for deferred dispatch).
|
||||
///
|
||||
/// Each variant represents a single logical key; modifier flags (ctrl/alt/shift)
|
||||
/// are carried separately by [`ClientRequest::KeyPress`].
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum KeyAction {
|
||||
/// A printable Unicode character.
|
||||
Char(char),
|
||||
/// The Enter / Return key.
|
||||
Enter,
|
||||
/// The Escape key.
|
||||
Escape,
|
||||
/// The Backspace key.
|
||||
Backspace,
|
||||
/// The Delete key.
|
||||
Delete,
|
||||
/// The Tab key.
|
||||
Tab,
|
||||
/// Up arrow.
|
||||
Up,
|
||||
/// Down arrow.
|
||||
Down,
|
||||
/// Left arrow.
|
||||
Left,
|
||||
/// Right arrow.
|
||||
Right,
|
||||
/// The Home key.
|
||||
Home,
|
||||
/// The End key.
|
||||
End,
|
||||
/// The Page Up key.
|
||||
PageUp,
|
||||
/// The Page Down key.
|
||||
PageDown,
|
||||
/// A function key F1–F255.
|
||||
Function(u8),
|
||||
}
|
||||
|
||||
@@ -36,29 +54,36 @@ pub enum KeyAction {
|
||||
// ClientRequest
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A message sent from the TUI client to the daemon.
|
||||
/// A message sent from the TUI client to the daemon over the IPC socket.
|
||||
///
|
||||
/// Each variant corresponds to a distinct client-originated event. The daemon
|
||||
/// processes it and responds with a [`DaemonFrame`].
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ClientRequest {
|
||||
/// Periodic heartbeat / tick event.
|
||||
/// Periodic heartbeat / tick event — keeps the daemon's event loop alive.
|
||||
Tick,
|
||||
/// A keyboard event with modifier flags.
|
||||
KeyPress {
|
||||
/// The resolved key that was pressed.
|
||||
key: KeyAction,
|
||||
/// Whether the Ctrl modifier was held.
|
||||
ctrl: bool,
|
||||
/// Whether the Alt modifier was held.
|
||||
alt: bool,
|
||||
/// Whether the Shift modifier was held.
|
||||
shift: bool,
|
||||
},
|
||||
/// A completed text submission (e.g. pressing Enter in the input bar).
|
||||
Submit(String),
|
||||
/// Pasted text content.
|
||||
/// Pasted text content from the system clipboard.
|
||||
Paste(String),
|
||||
/// Terminal resize notification.
|
||||
/// Terminal resize notification carrying the new (cols, rows).
|
||||
Resize(u16, u16),
|
||||
/// Graceful close / shutdown request.
|
||||
Close,
|
||||
/// Scroll the session view up one page or line.
|
||||
/// Scroll the session view up by one viewport.
|
||||
ScrollUp,
|
||||
/// Scroll the session view down one page or line.
|
||||
/// Scroll the session view down by one viewport.
|
||||
ScrollDown,
|
||||
}
|
||||
|
||||
@@ -131,22 +156,30 @@ pub struct StatePayload {
|
||||
/// A frame sent from the daemon to the client.
|
||||
///
|
||||
/// Every response from the daemon is one of these variants. The client
|
||||
/// dispatches on the variant to update its UI model.
|
||||
/// dispatches on the variant to update its UI model or perform side effects
|
||||
/// (e.g. clipboard copy).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum DaemonFrame {
|
||||
/// Full state update — the client should replace its entire local state.
|
||||
/// Full state update — the client should replace its entire local state
|
||||
/// with the enclosed [`StatePayload`].
|
||||
StateUpdate(Box<StatePayload>),
|
||||
/// A streaming token for incremental assistant response rendering.
|
||||
///
|
||||
/// The client appends this text to the last assistant message in its
|
||||
/// local message list.
|
||||
StreamToken(String),
|
||||
/// A system-level notification that doesn't alter the session state.
|
||||
SystemNote {
|
||||
/// The kind of system note (e.g. "info", "warning", "error").
|
||||
kind: String,
|
||||
/// The note content.
|
||||
/// The display message body.
|
||||
message: String,
|
||||
},
|
||||
/// Instructs the client to place `text` into the system clipboard.
|
||||
/// Instructs the client to place the enclosed text into the system
|
||||
/// clipboard.
|
||||
ClipboardCopy(String),
|
||||
/// Signals that the daemon has shut down / the session is complete.
|
||||
/// Signals that the daemon has shut down or the session is complete.
|
||||
/// The client should tear down its connection and return to the
|
||||
/// connection screen (or exit).
|
||||
Closed,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::conn::Connection;
|
||||
use anyhow::{Context, Result};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::path::Path;
|
||||
use tracing;
|
||||
|
||||
/// A Unix-socket IPC server.
|
||||
///
|
||||
@@ -38,6 +39,7 @@ impl IpcServer {
|
||||
let listener = UnixListener::bind(path)
|
||||
.with_context(|| format!("failed to bind Unix socket at {path:?}"))?;
|
||||
|
||||
tracing::info!("IPC server bound to {path:?}");
|
||||
Ok(Self { listener })
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user