arcature-cli 2026.2.0

Developer lifecycle CLI for Arcature applications.
Documentation
//! `arc schedule` — list scheduled jobs and their cadence (side-effect-free).
//!
//! Loads the metadata artifact (no application boot) and prints scheduled
//! jobs or JSON. Mirrors the `arc schedule` output contract: job, version,
//! cadence. Supports `--json`.

use crate::cli::OutputFormat;
use crate::error::CommandError;
use crate::metadata::{CadenceEntry, ScheduleEntry};
use crate::project;
use serde::Serialize;

pub(crate) fn execute(format: OutputFormat) -> Result<(), CommandError> {
    let project = project::discover()?;
    let artifact = crate::metadata::load(&project).map_err(CommandError::Metadata)?;
    let schedules: Vec<&ScheduleEntry> = artifact
        .modules
        .values()
        .flat_map(|m| m.schedules.iter())
        .collect();
    match format {
        OutputFormat::Human => print_human(&schedules),
        OutputFormat::Json => print_json(&schedules)?,
    }
    Ok(())
}

fn print_human(schedules: &[&ScheduleEntry]) {
    if schedules.is_empty() {
        println!("no scheduled jobs registered");
        return;
    }
    println!("{:<24} {:<8} CADENCE", "JOB", "VERSION");
    for schedule in schedules {
        println!(
            "{:<24} {:<8} {}",
            schedule.job,
            schedule.version,
            cadence_str(&schedule.cadence)
        );
    }
}

fn print_json(schedules: &[&ScheduleEntry]) -> Result<(), CommandError> {
    #[derive(Serialize)]
    struct Out<'a> {
        schedules: Vec<&'a ScheduleEntry>,
    }
    println!(
        "{}",
        serde_json::to_string_pretty(&Out {
            schedules: schedules.to_vec()
        })?
    );
    Ok(())
}

fn cadence_str(cadence: &CadenceEntry) -> String {
    match cadence {
        CadenceEntry::Every { seconds } => format!("every {seconds}s"),
        CadenceEntry::Daily { hour, minute } => format!("daily {hour:02}:{minute:02}"),
    }
}