1pub mod platform;
2pub mod plist;
3pub mod systemd;
4
5use std::process::Command;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8static VERBOSE: AtomicBool = AtomicBool::new(false);
9
10pub fn set_verbose(verbose: bool) {
12 VERBOSE.store(verbose, Ordering::SeqCst);
13}
14
15pub fn print_command(cmd: &Command) {
17 if VERBOSE.load(Ordering::SeqCst) {
18 let program = cmd.get_program().to_string_lossy();
19 let args: Vec<_> = cmd.get_args().map(|a| a.to_string_lossy()).collect();
20 eprintln!("+ {} {}", program, args.join(" "));
21 }
22}
23
24#[derive(Debug, Clone, Default)]
27pub struct CalendarSchedule {
28 pub month: Option<u8>,
30 pub day: Option<u8>,
32 pub weekday: Option<u8>,
34 pub hour: Option<u8>,
36 pub minute: Option<u8>,
38}
39
40impl CalendarSchedule {
41 pub fn to_systemd_oncalendar(&self) -> String {
44 let weekday_str = match self.weekday {
45 Some(0) => "Sun ",
46 Some(1) => "Mon ",
47 Some(2) => "Tue ",
48 Some(3) => "Wed ",
49 Some(4) => "Thu ",
50 Some(5) => "Fri ",
51 Some(6) => "Sat ",
52 _ => "",
53 };
54
55 let month = self
56 .month
57 .map(|m| format!("{:02}", m))
58 .unwrap_or_else(|| "*".to_string());
59 let day = self
60 .day
61 .map(|d| format!("{:02}", d))
62 .unwrap_or_else(|| "*".to_string());
63 let hour = self
64 .hour
65 .map(|h| format!("{:02}", h))
66 .unwrap_or_else(|| "*".to_string());
67 let minute = self
68 .minute
69 .map(|m| format!("{:02}", m))
70 .unwrap_or_else(|| "00".to_string());
71
72 format!("{weekday_str}*-{month}-{day} {hour}:{minute}:00")
73 }
74
75 pub fn to_launchd_dict(&self) -> Vec<(String, i64)> {
77 let mut entries = Vec::new();
78 if let Some(month) = self.month {
79 entries.push(("Month".to_string(), month as i64));
80 }
81 if let Some(day) = self.day {
82 entries.push(("Day".to_string(), day as i64));
83 }
84 if let Some(weekday) = self.weekday {
85 entries.push(("Weekday".to_string(), weekday as i64));
86 }
87 if let Some(hour) = self.hour {
88 entries.push(("Hour".to_string(), hour as i64));
89 }
90 if let Some(minute) = self.minute {
91 entries.push(("Minute".to_string(), minute as i64));
92 }
93 entries
94 }
95
96 pub fn display(&self) -> String {
98 let mut parts = Vec::new();
99
100 if let Some(weekday) = self.weekday {
101 let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
102 if let Some(day) = days.get(weekday as usize) {
103 parts.push(day.to_string());
104 }
105 }
106
107 if let Some(day) = self.day {
108 parts.push(format!("day {}", day));
109 }
110
111 if let Some(hour) = self.hour {
112 let minute = self.minute.unwrap_or(0);
113 parts.push(format!("{:02}:{:02}", hour, minute));
114 }
115
116 if parts.is_empty() {
117 "scheduled".to_string()
118 } else {
119 parts.join(" ")
120 }
121 }
122}
123
124#[derive(Debug, Clone)]
125pub struct ServiceDetails {
126 pub name: String,
127 pub program: String,
128 pub arguments: Vec<String>,
129 pub working_directory: Option<String>,
130 pub run_at_load: bool,
131 pub keep_alive: bool,
132 pub env_file: Option<String>,
133 pub env_vars: Vec<(String, String)>,
134 pub after: Vec<String>,
135 pub schedule: Option<CalendarSchedule>,
136}
137
138#[derive(Debug, Clone)]
139pub struct FsServiceDetails {
140 pub service: ServiceDetails,
141 pub path: String,
142 pub enabled: bool,
143 pub running: bool,
144}