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 is_verbose() -> bool {
17 VERBOSE.load(Ordering::SeqCst)
18}
19
20pub fn run_command(cmd: &mut Command) -> std::io::Result<std::process::Output> {
23 if is_verbose() {
24 let program = cmd.get_program().to_string_lossy();
25 let args: Vec<_> = cmd.get_args().map(|a| a.to_string_lossy()).collect();
26 eprintln!("+ {} {}", program, args.join(" "));
27 }
28 cmd.output()
29}
30
31pub fn spawn_command(cmd: &mut Command) -> std::io::Result<std::process::Child> {
34 if is_verbose() {
35 let program = cmd.get_program().to_string_lossy();
36 let args: Vec<_> = cmd.get_args().map(|a| a.to_string_lossy()).collect();
37 eprintln!("+ {} {}", program, args.join(" "));
38 }
39 cmd.spawn()
40}
41
42pub fn run_command_status(cmd: &mut Command) -> std::io::Result<std::process::ExitStatus> {
44 if is_verbose() {
45 let program = cmd.get_program().to_string_lossy();
46 let args: Vec<_> = cmd.get_args().map(|a| a.to_string_lossy()).collect();
47 eprintln!("+ {} {}", program, args.join(" "));
48 }
49 cmd.status()
50}
51
52#[derive(Debug, Clone, Default)]
55pub struct CalendarSchedule {
56 pub month: Option<u8>,
58 pub day: Option<u8>,
60 pub weekday: Option<u8>,
62 pub hour: Option<u8>,
64 pub minute: Option<u8>,
66}
67
68impl CalendarSchedule {
69 pub fn to_systemd_oncalendar(&self) -> String {
72 let weekday_str = match self.weekday {
73 Some(0) => "Sun ",
74 Some(1) => "Mon ",
75 Some(2) => "Tue ",
76 Some(3) => "Wed ",
77 Some(4) => "Thu ",
78 Some(5) => "Fri ",
79 Some(6) => "Sat ",
80 _ => "",
81 };
82
83 let month = self
84 .month
85 .map(|m| format!("{:02}", m))
86 .unwrap_or_else(|| "*".to_string());
87 let day = self
88 .day
89 .map(|d| format!("{:02}", d))
90 .unwrap_or_else(|| "*".to_string());
91 let hour = self
92 .hour
93 .map(|h| format!("{:02}", h))
94 .unwrap_or_else(|| "*".to_string());
95 let minute = self
96 .minute
97 .map(|m| format!("{:02}", m))
98 .unwrap_or_else(|| "00".to_string());
99
100 format!("{weekday_str}*-{month}-{day} {hour}:{minute}:00")
101 }
102
103 pub fn to_launchd_dict(&self) -> Vec<(String, i64)> {
105 let mut entries = Vec::new();
106 if let Some(month) = self.month {
107 entries.push(("Month".to_string(), month as i64));
108 }
109 if let Some(day) = self.day {
110 entries.push(("Day".to_string(), day as i64));
111 }
112 if let Some(weekday) = self.weekday {
113 entries.push(("Weekday".to_string(), weekday as i64));
114 }
115 if let Some(hour) = self.hour {
116 entries.push(("Hour".to_string(), hour as i64));
117 }
118 if let Some(minute) = self.minute {
119 entries.push(("Minute".to_string(), minute as i64));
120 }
121 entries
122 }
123
124 pub fn display(&self) -> String {
126 let mut parts = Vec::new();
127
128 if let Some(weekday) = self.weekday {
129 let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
130 if let Some(day) = days.get(weekday as usize) {
131 parts.push(day.to_string());
132 }
133 }
134
135 if let Some(day) = self.day {
136 parts.push(format!("day {}", day));
137 }
138
139 if let Some(hour) = self.hour {
140 let minute = self.minute.unwrap_or(0);
141 parts.push(format!("{:02}:{:02}", hour, minute));
142 }
143
144 if parts.is_empty() {
145 "scheduled".to_string()
146 } else {
147 parts.join(" ")
148 }
149 }
150}
151
152#[derive(Debug, Clone)]
153pub struct ServiceDetails {
154 pub name: String,
155 pub program: String,
156 pub arguments: Vec<String>,
157 pub working_directory: Option<String>,
158 pub run_at_load: bool,
159 pub keep_alive: bool,
160 pub env_file: Option<String>,
161 pub env_vars: Vec<(String, String)>,
162 pub after: Vec<String>,
163 pub schedule: Option<CalendarSchedule>,
164}
165
166#[derive(Debug, Clone)]
167pub struct FsServiceDetails {
168 pub service: ServiceDetails,
169 pub path: String,
170 pub enabled: bool,
171 pub running: bool,
172}