smriti/services/
install_method.rs1use std::path::Path;
16#[cfg(target_os = "linux")]
17use std::path::PathBuf;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
25#[allow(dead_code)]
26pub enum InstallMethod {
27 Portable,
31
32 MsiInstaller,
35
36 MacOsApp,
41
42 PackageManager { name: String, upgrade_cmd: String },
45
46 SourceBuild,
49
50 Unknown,
53}
54
55pub fn detect() -> InstallMethod {
58 let exe = match std::env::current_exe() {
59 Ok(p) => p,
60 Err(_) => return InstallMethod::Unknown,
61 };
62 classify_path(&exe)
63}
64
65#[allow(clippy::needless_return)]
67pub(crate) fn classify_path(exe: &Path) -> InstallMethod {
68 let path_str = exe.to_string_lossy();
69 let lower = path_str.to_lowercase();
70
71 if lower.contains("/target/debug/") || lower.contains("/target/release/") {
74 return InstallMethod::SourceBuild;
75 }
76 if lower.contains("\\target\\debug\\") || lower.contains("\\target\\release\\") {
77 return InstallMethod::SourceBuild;
78 }
79
80 #[cfg(target_os = "linux")]
82 {
83 if lower.ends_with(".appimage") || lower.contains("/squashfs-root/") {
84 return InstallMethod::Portable;
85 }
86 if lower.contains("/var/lib/flatpak/") || lower.starts_with("/app/bin/") {
87 return InstallMethod::PackageManager {
88 name: "flatpak".into(),
89 upgrade_cmd: "flatpak update in.smriti.app".into(),
90 };
91 }
92 if lower.starts_with("/snap/") {
93 return InstallMethod::PackageManager {
94 name: "snap".into(),
95 upgrade_cmd: "snap refresh smriti".into(),
96 };
97 }
98 if path_str == "/usr/bin/smriti" && PathBuf::from("/usr/lib/smriti").exists() {
100 return InstallMethod::PackageManager {
101 name: "apt".into(),
102 upgrade_cmd: "sudo apt update && sudo apt install --only-upgrade smriti".into(),
103 };
104 }
105 }
106
107 #[cfg(target_os = "macos")]
109 {
110 if lower.contains("/opt/homebrew/") || lower.contains("/usr/local/cellar/") {
111 return InstallMethod::PackageManager {
112 name: "brew".into(),
113 upgrade_cmd: "brew update && brew upgrade --cask smriti".into(),
114 };
115 }
116 if lower.contains(".app/contents/macos/")
117 && (lower.contains("/applications/") || lower.contains("/users/"))
118 {
119 return InstallMethod::MacOsApp;
120 }
121 }
122
123 #[cfg(target_os = "windows")]
125 {
126 if lower.contains("\\program files\\") || lower.contains("\\program files (x86)\\") {
128 return InstallMethod::MsiInstaller;
129 }
130 if lower.contains("\\windowsapps\\") {
132 return InstallMethod::PackageManager {
133 name: "msstore".into(),
134 upgrade_cmd: "Microsoft Store -> Updates".into(),
135 };
136 }
137 if lower.contains("\\scoop\\apps\\") {
139 return InstallMethod::PackageManager {
140 name: "scoop".into(),
141 upgrade_cmd: "scoop update smriti".into(),
142 };
143 }
144 return InstallMethod::Portable;
146 }
147
148 #[cfg(not(target_os = "windows"))]
151 {
152 InstallMethod::Unknown
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use std::path::PathBuf;
160
161 #[test]
162 fn source_build_detected_unix() {
163 let p = PathBuf::from("/home/user/code/smriti/target/release/smriti");
164 assert_eq!(classify_path(&p), InstallMethod::SourceBuild);
165 }
166
167 #[test]
168 fn source_build_detected_debug() {
169 let p = PathBuf::from("/home/user/code/smriti/target/debug/smriti");
170 assert_eq!(classify_path(&p), InstallMethod::SourceBuild);
171 }
172
173 #[test]
174 fn source_build_detected_windows() {
175 let p = PathBuf::from("C:\\Users\\foo\\smriti\\target\\release\\smriti.exe");
176 assert_eq!(classify_path(&p), InstallMethod::SourceBuild);
177 }
178
179 #[cfg(target_os = "linux")]
180 #[test]
181 fn appimage_detected() {
182 let p = PathBuf::from("/home/user/Applications/Smriti-x86_64.AppImage");
183 assert_eq!(classify_path(&p), InstallMethod::Portable);
184 }
185
186 #[cfg(target_os = "linux")]
187 #[test]
188 fn flatpak_detected() {
189 let p = PathBuf::from(
190 "/var/lib/flatpak/app/in.smriti.app/x86_64/stable/current/files/bin/smriti",
191 );
192 match classify_path(&p) {
193 InstallMethod::PackageManager { name, .. } => assert_eq!(name, "flatpak"),
194 other => panic!("expected flatpak, got {:?}", other),
195 }
196 }
197
198 #[cfg(target_os = "linux")]
199 #[test]
200 fn snap_detected() {
201 let p = PathBuf::from("/snap/smriti/current/usr/bin/smriti");
202 match classify_path(&p) {
203 InstallMethod::PackageManager { name, .. } => assert_eq!(name, "snap"),
204 other => panic!("expected snap, got {:?}", other),
205 }
206 }
207
208 #[cfg(target_os = "windows")]
209 #[test]
210 fn msi_installer_detected() {
211 let p = PathBuf::from("C:\\Program Files\\Smriti\\smriti.exe");
212 assert_eq!(classify_path(&p), InstallMethod::MsiInstaller);
213 }
214
215 #[cfg(target_os = "windows")]
216 #[test]
217 fn scoop_detected() {
218 let p = PathBuf::from("C:\\Users\\user\\scoop\\apps\\smriti\\current\\smriti.exe");
219 match classify_path(&p) {
220 InstallMethod::PackageManager { name, .. } => assert_eq!(name, "scoop"),
221 other => panic!("expected scoop, got {:?}", other),
222 }
223 }
224
225 #[cfg(target_os = "macos")]
226 #[test]
227 fn macos_app_bundle_detected() {
228 let p = PathBuf::from("/Applications/Smriti.app/Contents/MacOS/smriti");
229 assert_eq!(classify_path(&p), InstallMethod::MacOsApp);
230 }
231
232 #[cfg(target_os = "macos")]
233 #[test]
234 fn homebrew_cask_detected() {
235 let p = PathBuf::from("/opt/homebrew/Caskroom/smriti/1.0.0/smriti");
236 match classify_path(&p) {
237 InstallMethod::PackageManager { name, .. } => assert_eq!(name, "brew"),
238 other => panic!("expected brew, got {:?}", other),
239 }
240 }
241}