use anyhow::Result;
use onlyne_layout::RoleWorkspace;
use std::path::{Path, PathBuf};
use std::time::Duration;
pub const NOT_RUNNING: &str = "onlyne: client not running";
pub const NOT_CONNECTED: &str = "onlyne: client not connected";
pub const FAULT_SCAN_LIMIT: u32 = 10_000;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StatusReport {
pub uptime: Duration,
pub socket: PathBuf,
pub faults: usize,
pub connected: bool,
}
impl StatusReport {
pub fn line(&self) -> String {
format!(
"onlyne: client running uptime {}s socket {} faults {}",
self.uptime.as_secs(),
self.socket.display(),
self.faults
)
}
pub fn exit_code(&self) -> i32 {
if self.connected { 0 } else { 2 }
}
}
pub async fn status(workspace: &Path) -> Result<Option<StatusReport>> {
let layout = RoleWorkspace::resolve(workspace);
let socket = layout.socket_path();
let Some(connected) = crate::session::adapter_socket::server_link_state(&socket).await else {
return Ok(None);
};
let uptime = std::fs::metadata(&socket)
.and_then(|meta| meta.modified())
.ok()
.and_then(|modified| std::time::SystemTime::now().duration_since(modified).ok())
.unwrap_or_default();
Ok(Some(StatusReport {
uptime,
faults: fault_count(&layout)?,
connected,
socket,
}))
}
pub fn fault_count(layout: &RoleWorkspace) -> Result<usize> {
let path = layout.client_db_path();
if !path.exists() {
return Ok(0);
}
let store = onlyne_store::ClientStore::open(&path)?;
let events = store.events_since(0, FAULT_SCAN_LIMIT)?;
Ok(events
.iter()
.filter(|event| event.kind == "session_fault")
.count())
}
#[cfg(test)]
mod tests;