use std::time::Duration;
use tokio::net::UnixStream;
use super::bootstrap::ensure_daemon_running;
use super::client::connect_at;
use super::ipc::{DaemonRequest, DaemonRequestPayload};
use super::socket::daemon_socket_path;
const STOP_TIMEOUT: Duration = Duration::from_secs(10);
const STOP_POLL: Duration = Duration::from_millis(50);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DaemonStatus {
Running,
NotRunning,
}
pub async fn status() -> anyhow::Result<DaemonStatus> {
let socket = daemon_socket_path();
let client = match connect_at(&socket).await {
Ok(c) => c,
Err(_) => return Ok(DaemonStatus::NotRunning),
};
match client
.call_raw(DaemonRequest {
cwd: std::env::current_dir().unwrap_or_default(),
payload: DaemonRequestPayload::Ping,
})
.await
{
Ok(_) => Ok(DaemonStatus::Running),
Err(_) => Ok(DaemonStatus::NotRunning),
}
}
pub async fn stop() -> anyhow::Result<()> {
let socket = daemon_socket_path();
let client = match connect_at(&socket).await {
Ok(c) => c,
Err(_) => return Ok(()),
};
let _ = client
.call_raw(DaemonRequest {
cwd: std::env::current_dir().unwrap_or_default(),
payload: DaemonRequestPayload::Shutdown,
})
.await;
let deadline = std::time::Instant::now() + STOP_TIMEOUT;
loop {
if !socket.exists() || UnixStream::connect(&socket).await.is_err() {
return Ok(());
}
if std::time::Instant::now() >= deadline {
anyhow::bail!(
"daemon did not stop within {}s (socket {} still reachable)",
STOP_TIMEOUT.as_secs(),
socket.display(),
);
}
tokio::time::sleep(STOP_POLL).await;
}
}
pub async fn restart() -> anyhow::Result<()> {
restart_with(ensure_daemon_running).await
}
#[doc(hidden)]
pub async fn restart_with<F, Fut>(respawn: F) -> anyhow::Result<()>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = anyhow::Result<()>>,
{
stop().await?;
respawn().await
}