use cron_union::{union as union_crons, CronUnion};
use croner::Cron;
use crate::error::AppError;
pub(crate) fn normalize_schedule(expr: &str) -> String {
let trimmed = expr.trim();
if trimmed.starts_with('@') {
return trimmed.to_string();
}
let fields: Vec<&str> = trimmed.split_ascii_whitespace().collect();
match fields.len() {
6 | 7 => fields[1..6].join(" "),
_ => trimmed.to_string(),
}
}
pub(crate) fn validate_cron(expr: &str) -> Result<(), AppError> {
let normalized = normalize_schedule(expr.trim());
normalized
.parse::<Cron>()
.map_err(|err| AppError::BadRequest(format!("invalid cron expression: {err}")))?;
Ok(())
}
pub(crate) fn compiled_union(schedule: &str) -> Option<CronUnion> {
let trimmed = schedule.trim();
if matches!(trimmed, "@reboot" | "@midnight") {
return None;
}
let normalized = normalize_schedule(trimmed);
union_crons([normalized.as_str()]).ok()
}
#[cfg(test)]
#[path = "cron_tests.rs"]
mod cron_tests;