weft_core/service_manager/
mod.rs1use std::path::{Path, PathBuf};
2
3pub use platform::platform_service_manager;
4
5#[cfg(target_os = "windows")]
6mod platform {
7 pub use super::windows::WindowsServiceManager as PlatformImpl;
8 pub fn platform_service_manager() -> PlatformImpl {
9 PlatformImpl
10 }
11}
12
13#[cfg(target_os = "linux")]
14mod platform {
15 pub use super::linux::LinuxServiceManager as PlatformImpl;
16 pub fn platform_service_manager() -> PlatformImpl {
17 PlatformImpl
18 }
19}
20
21#[cfg(target_os = "macos")]
22mod platform {
23 pub use super::macos::MacosServiceManager as PlatformImpl;
24 pub fn platform_service_manager() -> PlatformImpl {
25 PlatformImpl
26 }
27}
28
29#[cfg(windows)]
30pub mod windows;
31#[cfg(target_os = "linux")]
32pub mod linux;
33#[cfg(target_os = "macos")]
34pub mod macos;
35
36#[derive(Debug, Clone)]
38pub struct ServiceInstallOptions {
39 pub binary_path: PathBuf,
43 pub config_dir: PathBuf,
45 pub data_dir: PathBuf,
47 pub service_name: String,
49}
50
51#[derive(Debug, Clone, serde::Serialize)]
52pub struct ServiceStatus {
53 pub installed: bool,
54 pub running: bool,
55 pub pid: Option<u32>,
56 pub binary_path: Option<PathBuf>,
57}
58
59pub trait PlatformServiceManager {
60 fn install(&self, opts: &ServiceInstallOptions) -> anyhow::Result<()>;
61 fn uninstall(&self, service_name: &str) -> anyhow::Result<()>;
62 fn start(&self, service_name: &str) -> anyhow::Result<()>;
63 fn stop(&self, service_name: &str) -> anyhow::Result<()>;
64 fn status(&self, service_name: &str) -> anyhow::Result<ServiceStatus>;
65}
66
67pub fn resolve_install_options(args: &[String]) -> anyhow::Result<ServiceInstallOptions> {
70 let current_exe = std::env::current_exe()
71 .map_err(|e| anyhow::anyhow!("cannot determine current executable: {}", e))?;
72
73 let mut dest_dir: Option<PathBuf> = None;
74 let mut config_dir: Option<PathBuf> = None;
75 let mut data_dir: Option<PathBuf> = None;
76 let mut service_name = "weft-core".to_string();
77 let mut mode_system = false;
78
79 let mut i = 0;
80 while i < args.len() {
81 match args[i].as_str() {
82 "--path" => {
83 dest_dir = Some(PathBuf::from(next_arg(args, i, "--path")?));
84 i += 2;
85 }
86 "--config" => {
87 config_dir = Some(PathBuf::from(next_arg(args, i, "--config")?));
88 i += 2;
89 }
90 "--data" => {
91 data_dir = Some(PathBuf::from(next_arg(args, i, "--data")?));
92 i += 2;
93 }
94 "--service-name" => {
95 service_name = next_arg(args, i, "--service-name")?.to_string();
96 i += 2;
97 }
98 "--mode=system" | "--mode" if args.get(i + 1).map(|s| s.as_str()) == Some("system") => {
99 mode_system = true;
100 i += if args[i] == "--mode" { 2 } else { 1 };
101 }
102 other => {
103 anyhow::bail!("unknown flag: {}", other);
104 }
105 }
106 }
107
108 if mode_system && dest_dir.is_none() {
109 dest_dir = Some(system_binary_dir());
110 }
111 if mode_system && config_dir.is_none() {
112 config_dir = Some(system_config_dir());
113 }
114 if mode_system && data_dir.is_none() {
115 data_dir = Some(system_data_dir());
116 }
117
118 let binary_path = if let Some(dir) = dest_dir {
120 let bin_name = current_exe
121 .file_name()
122 .ok_or_else(|| anyhow::anyhow!("cannot determine binary filename"))?;
123 let dest = dir.join(bin_name);
124 std::fs::create_dir_all(&dir)
125 .map_err(|e| anyhow::anyhow!("cannot create destination dir {:?}: {}", dir, e))?;
126 std::fs::copy(¤t_exe, &dest)
127 .map_err(|e| anyhow::anyhow!("cannot copy binary to {:?}: {}", dest, e))?;
128 println!("Copied binary to {}", dest.display());
129 dest
130 } else {
131 current_exe.clone()
132 };
133
134 let binary_dir = binary_path
136 .parent()
137 .unwrap_or_else(|| Path::new("."))
138 .to_path_buf();
139
140 let config_dir = config_dir.unwrap_or_else(|| binary_dir.join("config"));
141 let data_dir = data_dir.unwrap_or_else(|| binary_dir.join("data"));
142
143 Ok(ServiceInstallOptions {
144 binary_path,
145 config_dir,
146 data_dir,
147 service_name,
148 })
149}
150
151fn next_arg<'a>(args: &'a [String], i: usize, flag: &str) -> anyhow::Result<&'a str> {
152 args.get(i + 1)
153 .map(|s| s.as_str())
154 .ok_or_else(|| anyhow::anyhow!("{} requires a value", flag))
155}
156
157#[cfg(target_os = "windows")]
158fn system_binary_dir() -> PathBuf {
159 PathBuf::from(std::env::var("ProgramData").unwrap_or_else(|_| "C:\\ProgramData".into()))
160 .join("WEFT")
161}
162#[cfg(target_os = "windows")]
163fn system_config_dir() -> PathBuf {
164 system_binary_dir().join("config")
165}
166#[cfg(target_os = "windows")]
167fn system_data_dir() -> PathBuf {
168 system_binary_dir().join("data")
169}
170
171#[cfg(target_os = "linux")]
172fn system_binary_dir() -> PathBuf {
173 PathBuf::from("/usr/local/bin")
174}
175#[cfg(target_os = "linux")]
176fn system_config_dir() -> PathBuf {
177 PathBuf::from("/etc/weft")
178}
179#[cfg(target_os = "linux")]
180fn system_data_dir() -> PathBuf {
181 PathBuf::from("/var/lib/weft")
182}
183
184#[cfg(target_os = "macos")]
185fn system_binary_dir() -> PathBuf {
186 PathBuf::from("/usr/local/bin")
187}
188#[cfg(target_os = "macos")]
189fn system_config_dir() -> PathBuf {
190 PathBuf::from("/Library/Application Support/WEFT")
191}
192#[cfg(target_os = "macos")]
193fn system_data_dir() -> PathBuf {
194 PathBuf::from("/Library/Application Support/WEFT/data")
195}