Fix build and runtime issues
This commit includes: - Fixed XDG path handling to use place_runtime_file instead of get_runtime_path - Updated JSON deserialization for Tokio async compatibility in both client and daemon - Added missing dirs-next dependency for home directory access - Fixed return values in several functions - Created display module for client UI formatting - Fixed command-line argument conflicts in daemonmain
parent
f286cc3271
commit
bf4f757ef2
|
|
@ -15,4 +15,4 @@ tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
xdg = { workspace = true }
|
xdg = { workspace = true }
|
||||||
rustyline = "12.0"
|
rustyline = "11.0"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
//! Display utilities for the Cypraea client.
|
||||||
|
//!
|
||||||
|
//! This module contains functions for formatting and displaying information
|
||||||
|
//! from the Cypraea daemon.
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use cypraea_common::protocol::SessionInfo;
|
||||||
|
|
||||||
|
/// Format a session list for display.
|
||||||
|
pub fn format_session_list(sessions: &[SessionInfo]) -> String {
|
||||||
|
if sessions.is_empty() {
|
||||||
|
return "No active sessions.\n".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = String::new();
|
||||||
|
result.push_str("Available sessions:\n");
|
||||||
|
result.push_str("------------------\n");
|
||||||
|
|
||||||
|
for session in sessions {
|
||||||
|
result.push_str(&format!(
|
||||||
|
"- {} (created: {}, last used: {}, cmds: {}, {})\n",
|
||||||
|
session.id,
|
||||||
|
session.created_at.format("%Y-%m-%d %H:%M:%S"),
|
||||||
|
session.last_used.format("%Y-%m-%d %H:%M:%S"),
|
||||||
|
session.command_count,
|
||||||
|
if session.active { "active" } else { "idle" }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format session information for display.
|
||||||
|
pub fn format_session_info(session: &SessionInfo) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
result.push_str(&format!("Session: {}\n", session.id));
|
||||||
|
result.push_str(&format!("Working directory: {}\n", session.cwd));
|
||||||
|
result.push_str(&format!("Created: {}\n", session.created_at.format("%Y-%m-%d %H:%M:%S")));
|
||||||
|
result.push_str(&format!("Last used: {}\n", session.last_used.format("%Y-%m-%d %H:%M:%S")));
|
||||||
|
result.push_str(&format!("Commands executed: {}\n", session.command_count));
|
||||||
|
result.push_str(&format!("Status: {}\n", if session.active { "active" } else { "idle" }));
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print a message to stdout.
|
||||||
|
pub fn print_message(msg: &str) -> io::Result<()> {
|
||||||
|
print!("{}", msg);
|
||||||
|
io::stdout().flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print a message to stderr.
|
||||||
|
pub fn print_error(msg: &str) -> io::Result<()> {
|
||||||
|
eprint!("{}", msg);
|
||||||
|
io::stderr().flush()
|
||||||
|
}
|
||||||
|
|
@ -79,18 +79,30 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
// Spawn a task to read messages from the daemon
|
// Spawn a task to read messages from the daemon
|
||||||
let read_task = tokio::spawn(async move {
|
let read_task = tokio::spawn(async move {
|
||||||
let mut stream = Deserializer::from_reader(reader).into_iter::<DaemonMessage>();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
while let Some(msg_result) = stream.next() {
|
loop {
|
||||||
match msg_result {
|
// Read a line from the stream
|
||||||
Ok(msg) => {
|
buffer.clear();
|
||||||
debug!("Received message: {:?}", msg);
|
match reader.read_line(&mut buffer).await {
|
||||||
if tx.send(msg).await.is_err() {
|
Ok(0) => break, // End of stream
|
||||||
break;
|
Ok(_) => {
|
||||||
|
// Parse the message
|
||||||
|
match serde_json::from_str::<DaemonMessage>(&buffer) {
|
||||||
|
Ok(msg) => {
|
||||||
|
debug!("Received message: {:?}", msg);
|
||||||
|
if tx.send(msg).await.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error parsing message: {}", e);
|
||||||
|
// Continue trying to parse other messages
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error parsing message: {}", e);
|
error!("Error reading from daemon: {}", e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use xdg::BaseDirectories;
|
||||||
pub fn socket_path() -> Result<PathBuf> {
|
pub fn socket_path() -> Result<PathBuf> {
|
||||||
// Use XDG_RUNTIME_DIR if available, or fall back to a temporary directory
|
// Use XDG_RUNTIME_DIR if available, or fall back to a temporary directory
|
||||||
let xdg = BaseDirectories::new().context("Failed to initialize XDG base directories")?;
|
let xdg = BaseDirectories::new().context("Failed to initialize XDG base directories")?;
|
||||||
Ok(xdg.get_runtime_path().join("cypraea.sock"))
|
Ok(xdg.place_runtime_file("cypraea.sock")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the path to the Cypraea database directory.
|
/// Get the path to the Cypraea database directory.
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,4 @@ tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
xdg = { workspace = true }
|
xdg = { workspace = true }
|
||||||
|
dirs-next = "2.0"
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ impl Database {
|
||||||
)
|
)
|
||||||
.context("Failed to create commands table")?;
|
.context("Failed to create commands table")?;
|
||||||
|
|
||||||
Ok()
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Log a command execution.
|
/// Log a command execution.
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ struct Args {
|
||||||
database: Option<String>,
|
database: Option<String>,
|
||||||
|
|
||||||
/// Enable debug logging
|
/// Enable debug logging
|
||||||
#[clap(short, long)]
|
#[clap(short = 'v', long)]
|
||||||
debug: bool,
|
debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,5 +70,5 @@ async fn main() -> Result<()> {
|
||||||
.context("Socket server error")?;
|
.context("Socket server error")?;
|
||||||
|
|
||||||
info!("Daemon shutting down");
|
info!("Daemon shutting down");
|
||||||
Ok()
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ pub struct Session {
|
||||||
impl Session {
|
impl Session {
|
||||||
/// Create a new session.
|
/// Create a new session.
|
||||||
pub fn new(id: String) -> Self {
|
pub fn new(id: String) -> Self {
|
||||||
let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home = dirs_next::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -84,18 +84,38 @@ async fn handle_client(stream: UnixStream, session_manager: SessionManager) -> R
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process incoming messages
|
// Process incoming messages
|
||||||
let mut stream = Deserializer::from_reader(reader).into_iter::<ClientMessage>();
|
let mut buffer = String::new();
|
||||||
|
loop {
|
||||||
while let Some(msg_result) = stream.next() {
|
// Read a line from the stream
|
||||||
let msg = msg_result.context("Failed to parse client message")?;
|
buffer.clear();
|
||||||
debug!("Received message: {:?}", msg);
|
match reader.read_line(&mut buffer).await {
|
||||||
|
Ok(0) => break, // End of stream
|
||||||
if let Err(e) = process_message(msg, &session_manager, tx.clone()).await {
|
Ok(_) => {
|
||||||
error!("Error processing message: {}", e);
|
// Parse the message
|
||||||
let error_msg = DaemonMessage::Error {
|
match serde_json::from_str::<ClientMessage>(&buffer) {
|
||||||
message: e.to_string(),
|
Ok(msg) => {
|
||||||
};
|
debug!("Received message: {:?}", msg);
|
||||||
tx.send(error_msg).await.ok();
|
if let Err(e) = process_message(msg, &session_manager, tx.clone()).await {
|
||||||
|
error!("Error processing message: {}", e);
|
||||||
|
let error_msg = DaemonMessage::Error {
|
||||||
|
message: e.to_string(),
|
||||||
|
};
|
||||||
|
tx.send(error_msg).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to parse client message: {}", e);
|
||||||
|
let error_msg = DaemonMessage::Error {
|
||||||
|
message: format!("Invalid message format: {}", e),
|
||||||
|
};
|
||||||
|
tx.send(error_msg).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error reading from client: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue