mur_common/
schedule_claim.rs1use std::path::{Path, PathBuf};
7
8use crate::schedule::{Schedule, ScheduleExecutor, SchedulesFile};
9
10pub fn commander_pid_path() -> PathBuf {
12 dirs::home_dir()
13 .unwrap_or_default()
14 .join(".mur")
15 .join("commander")
16 .join("commander.pid")
17}
18
19pub fn is_commander_running() -> bool {
21 is_commander_running_at(&commander_pid_path())
22}
23
24pub fn is_commander_running_at(pid_path: &Path) -> bool {
26 let content = match std::fs::read_to_string(pid_path) {
27 Ok(c) => c,
28 Err(_) => return false,
29 };
30
31 let pid: i32 = match content.trim().parse() {
32 Ok(p) => p,
33 Err(_) => return false,
34 };
35
36 #[cfg(unix)]
38 {
39 unsafe { libc::kill(pid, 0) == 0 }
40 }
41 #[cfg(not(unix))]
42 {
43 false
45 }
46}
47
48pub fn schedules_path() -> PathBuf {
50 dirs::home_dir()
51 .unwrap_or_default()
52 .join(".mur")
53 .join("schedules.yaml")
54}
55
56pub fn load_schedules() -> Result<Vec<Schedule>, Box<dyn std::error::Error>> {
58 let path = schedules_path();
59 if !path.exists() {
60 return Ok(Vec::new());
61 }
62 let content = std::fs::read_to_string(&path)?;
63 let file: SchedulesFile = serde_yaml::from_str(&content)?;
64 Ok(file.schedules)
65}
66
67pub fn save_schedules(schedules: &[Schedule]) -> Result<(), Box<dyn std::error::Error>> {
69 let path = schedules_path();
70 if let Some(parent) = path.parent() {
71 std::fs::create_dir_all(parent)?;
72 }
73 let file = SchedulesFile {
74 schedules: schedules.to_vec(),
75 };
76 let yaml = serde_yaml::to_string(&file)?;
77 std::fs::write(&path, yaml)?;
78 Ok(())
79}
80
81pub fn claim_all_for_commander() -> Result<Vec<String>, Box<dyn std::error::Error>> {
84 let mut schedules = load_schedules()?;
85 let mut claimed = Vec::new();
86
87 for schedule in &mut schedules {
88 if schedule.executor != ScheduleExecutor::Commander {
89 claimed.push(schedule.workflow.clone());
90 schedule.executor = ScheduleExecutor::Commander;
91 }
92 }
93
94 if !claimed.is_empty() {
95 save_schedules(&schedules)?;
96 }
97
98 Ok(claimed)
99}
100
101pub fn release_all_from_commander() -> Result<Vec<String>, Box<dyn std::error::Error>> {
104 let mut schedules = load_schedules()?;
105 let mut released = Vec::new();
106
107 for schedule in &mut schedules {
108 if schedule.executor == ScheduleExecutor::Commander {
109 released.push(schedule.workflow.clone());
110 schedule.executor = ScheduleExecutor::SystemCron;
111 }
112 }
113
114 if !released.is_empty() {
115 save_schedules(&schedules)?;
116 }
117
118 Ok(released)
119}
120
121pub fn auto_detect_executor() -> ScheduleExecutor {
124 if is_commander_running() {
125 ScheduleExecutor::Commander
126 } else {
127 ScheduleExecutor::SystemCron
128 }
129}