pub mod human;
pub mod json;
use crate::pipeline::{ActionStatus, RuleOutcome};
use serde::Serialize;
use std::path::Path;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
Human,
Json,
}
pub struct InitOutput {
pub path: std::path::PathBuf,
pub uuid: Uuid,
}
#[derive(Serialize)]
pub struct CheckOutput {
pub frostx_version: &'static str,
pub project: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub path: String,
pub uuid: String,
pub inactive_seconds: i64,
pub rules: Vec<RuleCheckOutput>,
}
#[derive(Serialize)]
pub struct RuleCheckOutput {
pub index: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub after: String,
pub after_seconds: i64,
pub triggered: bool,
pub remaining_seconds: i64,
pub actions: Vec<ActionCheckOutput>,
pub completed_once: bool,
}
#[derive(Serialize, Clone)]
pub struct ActionCheckOutput {
pub name: String,
pub status: String,
pub message: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum DailySource {
Fresh,
Cached,
NotRun,
}
#[derive(Serialize)]
pub struct DailyCheckOutput<'a> {
pub frostx_version: &'static str,
pub daily_source: DailySource,
pub results: &'a [CheckOutput],
}
#[derive(Serialize)]
pub struct DailyRunOutput<'a> {
pub frostx_version: &'static str,
pub daily_source: DailySource,
pub actions: &'a [RunActionOutput],
}
#[derive(Serialize)]
pub struct RunActionOutput {
pub frostx_version: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub project: Option<String>,
pub rule: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub rule_name: Option<String>,
pub action: String,
pub status: String,
pub message: String,
}
#[derive(Serialize)]
pub struct DoctorOutput {
pub frostx_version: &'static str,
pub valid: bool,
pub errors: Vec<DoctorItem>,
pub warnings: Vec<DoctorItem>,
}
#[derive(Serialize)]
pub struct DoctorItem {
pub field: String,
pub message: String,
}
#[derive(Serialize)]
pub struct GcOutput {
pub frostx_version: &'static str,
pub orphaned: Vec<GcEntry>,
pub removed: usize,
}
#[derive(Serialize)]
pub struct GcEntry {
pub state_file: String,
pub reason: String,
pub path: String,
}
#[derive(Serialize)]
pub struct ProjectEntry {
pub uuid: String,
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_scan: Option<String>,
}
#[derive(Serialize)]
pub struct ProjectsListOutput {
pub frostx_version: &'static str,
pub projects: Vec<ProjectEntry>,
}
#[derive(Serialize)]
pub struct ProjectAddOutput {
pub frostx_version: &'static str,
pub added: Vec<ProjectEntry>,
pub skipped: Vec<ProjectAddSkip>,
}
#[derive(Serialize)]
pub struct ProjectAddSkip {
pub path: String,
pub reason: String,
}
#[derive(Serialize)]
pub struct ProjectRmOutput {
pub frostx_version: &'static str,
pub uuid: String,
pub path: String,
}
pub const FROSTX_VERSION: &str = env!("CARGO_PKG_VERSION");
#[must_use]
pub fn build_check_output(
project_name: &str,
project_description: Option<&str>,
project_path: &Path,
uuid: Uuid,
inactive_seconds: i64,
rule_outcomes: &[RuleOutcome],
) -> CheckOutput {
let rules = rule_outcomes
.iter()
.enumerate()
.map(|(i, ro)| RuleCheckOutput {
index: i + 1,
name: ro.name.clone(),
after: ro.after.to_string(),
after_seconds: ro.after_seconds,
triggered: ro.triggered,
remaining_seconds: ro.remaining_seconds,
actions: ro
.action_outcomes
.iter()
.map(|ao| ActionCheckOutput {
name: ao.name.clone(),
status: ao.status.as_str().to_string(),
message: ao.message.clone(),
})
.collect(),
completed_once: ro.completed_once,
})
.collect();
CheckOutput {
frostx_version: FROSTX_VERSION,
project: project_name.to_string(),
description: project_description.map(str::to_string),
path: project_path.display().to_string(),
uuid: uuid.to_string(),
inactive_seconds,
rules,
}
}
impl ActionStatus {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Ok => "ok",
Self::Failed => "failed",
Self::Skipped => "skipped",
Self::Completed => "completed",
Self::DryRun => "dry_run",
}
}
}