Skip to main content

actionqueue_cli/cmd/
daemon.rs

1//! Daemon command execution path.
2
3use std::net::SocketAddr;
4
5use serde_json::json;
6
7use crate::args::DaemonArgs;
8use crate::cmd::{resolve_data_dir, CliError, CommandOutput};
9
10/// Executes daemon bootstrap command flow.
11pub fn run(args: DaemonArgs) -> Result<CommandOutput, CliError> {
12    let mut config = actionqueue_daemon::config::DaemonConfig::default();
13    let data_dir = resolve_data_dir(args.data_dir.as_deref());
14    config.data_dir = data_dir.clone();
15
16    if let Some(bind) = args.bind.as_deref() {
17        config.bind_address = parse_socket_addr(bind, "bind_address")?;
18    }
19    if let Some(metrics_bind) = args.metrics_bind.as_deref() {
20        config.metrics_bind = Some(parse_socket_addr(metrics_bind, "metrics_bind")?);
21    }
22    config.enable_control = args.enable_control;
23
24    config.validate().map_err(|error| {
25        CliError::validation(
26            "daemon_config_invalid",
27            format!("daemon configuration rejected: {error}"),
28        )
29    })?;
30
31    let state = actionqueue_daemon::bootstrap::bootstrap(config).map_err(|error| {
32        CliError::runtime("daemon_bootstrap_failed", format!("daemon bootstrap failed: {error}"))
33    })?;
34
35    let ready = state.ready_status();
36    let metrics_bind = state.config().metrics_bind.map(|addr| addr.to_string());
37
38    if args.json {
39        return Ok(CommandOutput::Json(json!({
40            "command": "daemon",
41            "data_dir": data_dir.display().to_string(),
42            "bind_address": state.config().bind_address.to_string(),
43            "metrics_bind": metrics_bind,
44            "control_enabled": state.config().enable_control,
45            "ready": ready.is_ready(),
46            "ready_reason": ready.reason(),
47        })));
48    }
49
50    let lines = [
51        "command=daemon".to_string(),
52        format!("data_dir={}", data_dir.display()),
53        format!("bind_address={}", state.config().bind_address),
54        format!("metrics_bind={}", metrics_bind.as_deref().unwrap_or("disabled")),
55        format!("control_enabled={}", state.config().enable_control),
56        format!("ready={}", ready.is_ready()),
57        format!("ready_reason={}", ready.reason()),
58    ];
59    Ok(CommandOutput::Text(lines.join("\n")))
60}
61
62fn parse_socket_addr(raw: &str, field: &str) -> Result<SocketAddr, CliError> {
63    raw.parse::<SocketAddr>().map_err(|error| {
64        CliError::validation("invalid_socket_address", format!("invalid {field} '{raw}': {error}"))
65    })
66}