use car_scheduler::os_schedule::LABEL_PREFIX;
use car_scheduler::{OsScheduleSpec, Task, TaskStore};
use serde_json::json;
fn task_store() -> TaskStore {
TaskStore::new(&TaskStore::default_path())
}
fn os_spec(task_json: &str, program: &str, args_json: &str) -> Result<(Task, OsScheduleSpec), String> {
let task: Task =
serde_json::from_str(task_json).map_err(|e| format!("invalid task JSON: {e}"))?;
let args: Vec<String> =
serde_json::from_str(args_json).map_err(|e| format!("invalid args JSON (expected a string array): {e}"))?;
let spec = OsScheduleSpec::from_task(&task, program, args).map_err(|e| e.to_string())?;
Ok((task, spec))
}
pub fn render_os_schedule(
task_json: &str,
program: &str,
args_json: &str,
) -> Result<String, String> {
let (_task, spec) = os_spec(task_json, program, args_json)?;
let (plist, plist_err) = match spec.render_launchd_plist() {
Ok(s) => (Some(s), None),
Err(e) => (None, Some(e.to_string())),
};
let (cron, cron_err) = match spec.render_crontab_line() {
Ok(s) => (Some(s), None),
Err(e) => (None, Some(e.to_string())),
};
Ok(json!({
"label": spec.label,
"launchd_plist": plist,
"launchd_error": plist_err,
"crontab_line": cron,
"crontab_error": cron_err,
})
.to_string())
}
pub fn install_os_schedule(
task_json: &str,
program: &str,
args_json: &str,
) -> Result<String, String> {
let (task, spec) = os_spec(task_json, program, args_json)?;
task_store()
.save(&task)
.map_err(|e| format!("could not persist task for reconcile tracking: {e}"))?;
let installed = spec.install().map_err(|e| e.to_string())?;
serde_json::to_string(&installed).map_err(|e| e.to_string())
}
pub fn uninstall_os_schedule(label_or_id: &str) -> Result<String, String> {
let label = if label_or_id.starts_with(LABEL_PREFIX) {
label_or_id.to_string()
} else {
format!("{LABEL_PREFIX}{label_or_id}")
};
let removed = car_scheduler::uninstall(&label).map_err(|e| e.to_string())?;
Ok(json!({ "label": label, "removed": removed }).to_string())
}
pub fn list_os_schedules() -> Result<String, String> {
let labels = car_scheduler::list_installed().map_err(|e| e.to_string())?;
serde_json::to_string(&labels).map_err(|e| e.to_string())
}
pub fn reconcile_os_schedules() -> Result<String, String> {
let tasks = task_store().try_list().map_err(|e| {
format!("could not read task store at ~/.car/tasks (refusing to reap schedules): {e}")
})?;
let report = car_scheduler::reconcile_with_tasks(&tasks).map_err(|e| e.to_string())?;
serde_json::to_string(&report).map_err(|e| e.to_string())
}
pub fn create_task(
name: &str,
prompt: &str,
trigger: Option<&str>,
schedule: Option<&str>,
system_prompt: Option<&str>,
) -> Result<String, String> {
let mut task = car_scheduler::Task::new(name, prompt);
if let Some(t) = trigger {
let trigger_type = match t {
"once" => car_scheduler::TaskTrigger::Once,
"cron" => car_scheduler::TaskTrigger::Cron,
"interval" => car_scheduler::TaskTrigger::Interval,
"file_watch" => car_scheduler::TaskTrigger::FileWatch,
_ => car_scheduler::TaskTrigger::Manual,
};
task = task.with_trigger(trigger_type, schedule.unwrap_or(""));
}
if let Some(sp) = system_prompt {
task = task.with_system_prompt(sp);
}
serde_json::to_string(&task).map_err(|e| e.to_string())
}