use std::process::ExitCode;
use clap::Args;
use serde::{Deserialize, Serialize};
use crate::client::post_json;
use crate::config::ResolvedContext;
use crate::output::OutputFormat;
#[derive(Debug, Args)]
pub struct RunRetentionArgs {
#[arg(long)]
pub dry_run: bool,
}
#[derive(Debug, Serialize)]
struct RunRetentionRequest {
dry_run: bool,
}
#[derive(Debug, Deserialize, Serialize)]
struct RetentionRunStatsDto {
ran_at: String,
hot_rows: u64,
compressed_rows: u64,
archived_rows: u64,
dropped_rows: u64,
freed_bytes: u64,
dry_run: bool,
}
const ENDPOINT: &str = "/api/v1/admin/retention-policy/run";
pub fn dispatch(args: RunRetentionArgs, ctx: &ResolvedContext, output: OutputFormat) -> ExitCode {
let rt = tokio::runtime::Runtime::new().expect("failed to create tokio runtime");
let req = RunRetentionRequest { dry_run: args.dry_run };
let stats: RetentionRunStatsDto = match rt.block_on(post_json(ctx, ENDPOINT, &req)) {
Ok(stats) => stats,
Err(err) => {
eprintln!("aasm admin run-retention: {err}");
return ExitCode::FAILURE;
}
};
let rendered = match output {
OutputFormat::Yaml => serde_yaml::to_string(&stats).expect("serialize stats as YAML"),
OutputFormat::Table | OutputFormat::Json => {
serde_json::to_string_pretty(&stats).expect("serialize stats as JSON")
}
};
println!("{rendered}");
ExitCode::SUCCESS
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_serializes_dry_run_true() {
let body = serde_json::to_value(RunRetentionRequest { dry_run: true }).expect("serialize");
assert_eq!(body, serde_json::json!({"dry_run": true}));
}
#[test]
fn request_serializes_dry_run_false() {
let body = serde_json::to_value(RunRetentionRequest { dry_run: false }).expect("serialize");
assert_eq!(body, serde_json::json!({"dry_run": false}));
}
#[test]
fn response_deserializes_full_stats() {
let payload = serde_json::json!({
"ran_at": "2026-05-23T12:34:56Z",
"hot_rows": 100,
"compressed_rows": 20,
"archived_rows": 5,
"dropped_rows": 3,
"freed_bytes": 4096,
"dry_run": false
});
let stats: RetentionRunStatsDto = serde_json::from_value(payload).expect("deserialize");
assert_eq!(stats.ran_at, "2026-05-23T12:34:56Z");
assert_eq!(stats.hot_rows, 100);
assert_eq!(stats.compressed_rows, 20);
assert_eq!(stats.archived_rows, 5);
assert_eq!(stats.dropped_rows, 3);
assert_eq!(stats.freed_bytes, 4096);
assert!(!stats.dry_run);
}
#[test]
fn endpoint_matches_openapi_path() {
assert_eq!(ENDPOINT, "/api/v1/admin/retention-policy/run");
}
}