use std::io::{self, Write};
use std::process::ExitCode;
use anyhow::anyhow;
use clap::Parser;
use crate::daemon::lifecycle::{RestartOutcome, STOP_TIMEOUT, StatusReport};
use crate::daemon::{self};
use crate::workflow::Workflow;
#[derive(Debug, Parser)]
pub struct LifecycleArgs {}
#[derive(Debug, Parser)]
pub struct RestartArgs {
#[arg(long)]
pub port: Option<u16>,
#[arg(long, default_value = "127.0.0.1")]
pub bind_address: String,
}
pub fn status(workflow: Workflow, _args: LifecycleArgs) -> ExitCode {
match status_inner(&workflow) {
Ok(report) => {
render_status(&report);
ExitCode::SUCCESS
},
Err(err) => {
let _ = writeln!(io::stderr(), "vik status failed: {err:#}");
ExitCode::from(1)
},
}
}
fn status_inner(workflow: &Workflow) -> anyhow::Result<StatusReport> {
let path = workflow.workspace().service_state_file().to_path_buf();
daemon::lifecycle::status(&path).map_err(|err| anyhow!(err))
}
fn render_status(report: &StatusReport) {
let stdout = io::stdout();
let mut handle = stdout.lock();
let _ = writeln!(handle, "status: {}", report.status.as_str());
if let Some(state) = &report.state {
let _ = writeln!(handle, "state_file: {}", report.state_path.display());
let _ = writeln!(handle, "pid: {}", state.pid);
let _ = writeln!(handle, "bind_address: {}:{}", state.bind_address, state.port);
let _ = writeln!(handle, "started_at: {}", state.started_at);
let _ = writeln!(handle, "log_dir: {}", state.log_dir.display());
let _ = writeln!(handle, "sessions_dir: {}", state.sessions_dir.display());
let _ = writeln!(handle, "workflow_path: {}", state.workflow_path.display());
let _ = writeln!(handle, "command: {}", state.command);
} else {
let _ = writeln!(handle, "state_file: {}", report.state_path.display());
}
}
pub fn stop(workflow: Workflow, _args: LifecycleArgs) -> ExitCode {
match stop_inner(&workflow) {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
let _ = writeln!(io::stderr(), "vik stop failed: {err:#}");
ExitCode::from(1)
},
}
}
fn stop_inner(workflow: &Workflow) -> anyhow::Result<()> {
let path = workflow.workspace().service_state_file().to_path_buf();
daemon::lifecycle::stop(&path, STOP_TIMEOUT).map_err(|err| anyhow!(err))?;
let _ = writeln!(io::stdout(), "daemon stopped");
Ok(())
}
pub fn restart(workflow: Workflow, args: RestartArgs) -> ExitCode {
match restart_stop_phase(&workflow) {
Ok(RestartOutcome::Stopped) => {
let _ = writeln!(io::stdout(), "daemon stopped; starting a fresh one");
},
Ok(RestartOutcome::NotRunning) => {
let _ = writeln!(io::stdout(), "no daemon was running; starting one");
},
Err(err) => {
let _ = writeln!(io::stderr(), "vik restart failed: {err:#}");
return ExitCode::from(1);
},
}
super::run::execute(
workflow,
super::run::RunArgs {
port: args.port,
bind_address: args.bind_address,
detached: true,
},
)
}
fn restart_stop_phase(workflow: &Workflow) -> anyhow::Result<RestartOutcome> {
let path = workflow.workspace().service_state_file().to_path_buf();
daemon::lifecycle::restart_stop_phase(&path, STOP_TIMEOUT).map_err(|err| anyhow!(err))
}
pub fn uninstall(workflow: Workflow, _args: LifecycleArgs) -> ExitCode {
match uninstall_inner(&workflow) {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
let _ = writeln!(io::stderr(), "vik uninstall failed: {err:#}");
ExitCode::from(1)
},
}
}
fn uninstall_inner(workflow: &Workflow) -> anyhow::Result<()> {
let path = workflow.workspace().service_state_file().to_path_buf();
daemon::lifecycle::uninstall(&path, STOP_TIMEOUT).map_err(|err| anyhow!(err))?;
let _ = writeln!(io::stdout(), "daemon uninstalled (state file removed if present)");
Ok(())
}