1use std::collections::HashMap;
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
6pub enum SystemProxyCapability {
7 InspectEnvironment,
9 InspectMacosNetworksetup,
11 ApplyMacosNetworksetup,
13 InspectWindowsInternetSettings,
15 ApplyWindowsInternetSettings,
17 InspectGnomeSettings,
19 ApplyGnomeSettings,
21 InspectKdeSettings,
23 ApplyKdeSettings,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
29pub enum SystemProxyStatus {
30 Available,
32 MissingPrivilege,
34 UnsupportedPlatform,
36 ToolMissing,
38 DisabledAtCompileTime,
40}
41
42impl fmt::Display for SystemProxyCapability {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::InspectEnvironment => write!(f, "InspectEnvironment"),
46 Self::InspectMacosNetworksetup => write!(f, "InspectMacosNetworksetup"),
47 Self::ApplyMacosNetworksetup => write!(f, "ApplyMacosNetworksetup"),
48 Self::InspectWindowsInternetSettings => write!(f, "InspectWindowsInternetSettings"),
49 Self::ApplyWindowsInternetSettings => write!(f, "ApplyWindowsInternetSettings"),
50 Self::InspectGnomeSettings => write!(f, "InspectGnomeSettings"),
51 Self::ApplyGnomeSettings => write!(f, "ApplyGnomeSettings"),
52 Self::InspectKdeSettings => write!(f, "InspectKdeSettings"),
53 Self::ApplyKdeSettings => write!(f, "ApplyKdeSettings"),
54 }
55 }
56}
57
58impl fmt::Display for SystemProxyStatus {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 Self::Available => write!(f, "available"),
62 Self::MissingPrivilege => write!(f, "missing privilege"),
63 Self::UnsupportedPlatform => write!(f, "unsupported platform"),
64 Self::ToolMissing => write!(f, "tool missing"),
65 Self::DisabledAtCompileTime => write!(f, "disabled at compile time"),
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
72pub struct SystemProxyCapabilityReport {
73 pub capability: SystemProxyCapability,
74 pub status: SystemProxyStatus,
75}
76
77impl fmt::Display for SystemProxyCapabilityReport {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}: {}", self.capability, self.status)
80 }
81}
82
83pub fn check_system_proxy_capability(cap: SystemProxyCapability) -> SystemProxyStatus {
85 match cap {
86 SystemProxyCapability::InspectEnvironment => check_env_inspection(),
87 SystemProxyCapability::InspectMacosNetworksetup => check_tool_available("networksetup"),
88 SystemProxyCapability::ApplyMacosNetworksetup => check_tool_available("networksetup"),
89 SystemProxyCapability::InspectWindowsInternetSettings => check_windows_internet_settings(),
90 SystemProxyCapability::ApplyWindowsInternetSettings => check_windows_internet_settings(),
91 SystemProxyCapability::InspectGnomeSettings => check_tool_available("gsettings"),
92 SystemProxyCapability::ApplyGnomeSettings => check_tool_available("gsettings"),
93 SystemProxyCapability::InspectKdeSettings => check_tool_available("kwriteconfig5"),
94 SystemProxyCapability::ApplyKdeSettings => check_tool_available("kwriteconfig5"),
95 }
96}
97
98pub fn check_system_proxy_capability_with_overrides(
100 cap: SystemProxyCapability,
101 overrides: Option<&HashMap<SystemProxyCapability, SystemProxyStatus>>,
102) -> SystemProxyStatus {
103 if let Some(overrides) = overrides {
104 if let Some(status) = overrides.get(&cap) {
105 return status.clone();
106 }
107 }
108 check_system_proxy_capability(cap)
109}
110
111pub fn system_proxy_platform_info() -> Vec<SystemProxyCapabilityReport> {
113 ALL_SYSTEM_PROXY_CAPABILITIES
114 .iter()
115 .map(|&cap| SystemProxyCapabilityReport {
116 capability: cap,
117 status: check_system_proxy_capability(cap),
118 })
119 .collect()
120}
121
122const ALL_SYSTEM_PROXY_CAPABILITIES: &[SystemProxyCapability] = &[
124 SystemProxyCapability::InspectEnvironment,
125 SystemProxyCapability::InspectMacosNetworksetup,
126 SystemProxyCapability::ApplyMacosNetworksetup,
127 SystemProxyCapability::InspectWindowsInternetSettings,
128 SystemProxyCapability::ApplyWindowsInternetSettings,
129 SystemProxyCapability::InspectGnomeSettings,
130 SystemProxyCapability::ApplyGnomeSettings,
131 SystemProxyCapability::InspectKdeSettings,
132 SystemProxyCapability::ApplyKdeSettings,
133];
134
135fn check_env_inspection() -> SystemProxyStatus {
140 SystemProxyStatus::Available
141}
142
143fn check_tool_available(tool: &str) -> SystemProxyStatus {
144 #[cfg(unix)]
145 {
146 check_tool_available_unix(tool)
147 }
148 #[cfg(not(unix))]
149 {
150 let _ = tool;
151 SystemProxyStatus::UnsupportedPlatform
152 }
153}
154
155#[cfg(unix)]
156fn check_tool_available_unix(tool: &str) -> SystemProxyStatus {
157 use std::process::Command;
158
159 match Command::new("which").arg(tool).output() {
160 Ok(output) if output.status.success() => SystemProxyStatus::Available,
161 Ok(_) => SystemProxyStatus::ToolMissing,
162 Err(_) => SystemProxyStatus::ToolMissing,
163 }
164}
165
166fn check_windows_internet_settings() -> SystemProxyStatus {
167 #[cfg(target_os = "windows")]
168 {
169 SystemProxyStatus::Available
170 }
171 #[cfg(not(target_os = "windows"))]
172 {
173 SystemProxyStatus::UnsupportedPlatform
174 }
175}
176
177pub fn format_system_proxy_capability_report(reports: &[SystemProxyCapabilityReport]) -> String {
179 let mut out = String::from("System proxy capabilities:\n");
180 for report in reports {
181 out.push_str(&format!(" {}: {}\n", report.capability, report.status));
182 }
183 out
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn display_system_proxy_capability() {
192 assert_eq!(
193 SystemProxyCapability::InspectEnvironment.to_string(),
194 "InspectEnvironment"
195 );
196 assert_eq!(
197 SystemProxyCapability::ApplyMacosNetworksetup.to_string(),
198 "ApplyMacosNetworksetup"
199 );
200 }
201
202 #[test]
203 fn display_system_proxy_status() {
204 assert_eq!(SystemProxyStatus::Available.to_string(), "available");
205 assert_eq!(
206 SystemProxyStatus::MissingPrivilege.to_string(),
207 "missing privilege"
208 );
209 assert_eq!(
210 SystemProxyStatus::UnsupportedPlatform.to_string(),
211 "unsupported platform"
212 );
213 assert_eq!(SystemProxyStatus::ToolMissing.to_string(), "tool missing");
214 assert_eq!(
215 SystemProxyStatus::DisabledAtCompileTime.to_string(),
216 "disabled at compile time"
217 );
218 }
219
220 #[test]
221 fn capability_report_display() {
222 let report = SystemProxyCapabilityReport {
223 capability: SystemProxyCapability::InspectEnvironment,
224 status: SystemProxyStatus::Available,
225 };
226 assert_eq!(report.to_string(), "InspectEnvironment: available");
227 }
228
229 #[test]
230 fn env_inspection_always_available() {
231 assert_eq!(
232 check_system_proxy_capability(SystemProxyCapability::InspectEnvironment),
233 SystemProxyStatus::Available
234 );
235 }
236
237 #[test]
238 fn override_returns_override_value() {
239 let mut overrides = HashMap::new();
240 overrides.insert(
241 SystemProxyCapability::ApplyMacosNetworksetup,
242 SystemProxyStatus::ToolMissing,
243 );
244
245 assert_eq!(
246 check_system_proxy_capability_with_overrides(
247 SystemProxyCapability::ApplyMacosNetworksetup,
248 Some(&overrides)
249 ),
250 SystemProxyStatus::ToolMissing
251 );
252 }
253
254 #[test]
255 fn override_does_not_affect_unset_capabilities() {
256 let mut overrides = HashMap::new();
257 overrides.insert(
258 SystemProxyCapability::ApplyMacosNetworksetup,
259 SystemProxyStatus::Available,
260 );
261
262 assert_eq!(
263 check_system_proxy_capability_with_overrides(
264 SystemProxyCapability::ApplyMacosNetworksetup,
265 Some(&overrides)
266 ),
267 SystemProxyStatus::Available
268 );
269
270 let real_status = check_system_proxy_capability_with_overrides(
271 SystemProxyCapability::InspectEnvironment,
272 Some(&overrides),
273 );
274 assert_eq!(real_status, SystemProxyStatus::Available);
275 }
276
277 #[test]
278 fn platform_info_returns_all_capabilities() {
279 let info = system_proxy_platform_info();
280 assert_eq!(info.len(), 9);
281
282 let names: Vec<_> = info.iter().map(|r| r.capability.to_string()).collect();
283 assert!(names.contains(&"InspectEnvironment".to_string()));
284 assert!(names.contains(&"InspectMacosNetworksetup".to_string()));
285 }
286
287 #[test]
288 fn format_report_contains_names() {
289 let info = system_proxy_platform_info();
290 let formatted = format_system_proxy_capability_report(&info);
291 assert!(formatted.contains("System proxy capabilities:"));
292 assert!(formatted.contains("InspectEnvironment"));
293 }
294
295 #[test]
296 fn windows_internet_settings_unsupported_on_non_windows() {
297 #[cfg(not(target_os = "windows"))]
298 {
299 assert_eq!(
300 check_system_proxy_capability(
301 SystemProxyCapability::InspectWindowsInternetSettings
302 ),
303 SystemProxyStatus::UnsupportedPlatform
304 );
305 }
306 }
307
308 #[cfg(target_os = "macos")]
309 #[test]
310 fn macos_networksetup_available_on_macos() {
311 assert_eq!(
312 check_system_proxy_capability(SystemProxyCapability::InspectMacosNetworksetup),
313 SystemProxyStatus::Available
314 );
315 }
316}