pacsea/index/
installed.rs1use super::installed_lock;
2
3pub async fn refresh_installed_cache() {
14 if let Ok(Ok(body)) =
15 tokio::task::spawn_blocking(|| crate::util::pacman::run_pacman(&["-Qq"])).await
16 {
17 let set: std::collections::HashSet<String> =
18 body.lines().map(|s| s.trim().to_string()).collect();
19 if let Ok(mut g) = installed_lock().write() {
20 *g = set;
21 }
22 }
23}
24
25#[must_use]
36pub fn is_installed(name: &str) -> bool {
37 installed_lock()
38 .read()
39 .ok()
40 .is_some_and(|s| s.contains(name))
41}
42
43#[cfg(test)]
44mod tests {
45 #[test]
56 fn is_installed_returns_false_when_uninitialized_or_missing() {
57 let _guard = crate::global_test_mutex()
58 .lock()
59 .unwrap_or_else(std::sync::PoisonError::into_inner);
60 if let Ok(mut g) = super::installed_lock().write() {
61 g.clear();
62 }
63 assert!(!super::is_installed("foo"));
64 }
65
66 #[test]
77 fn is_installed_checks_membership_in_cached_set() {
78 let _guard = crate::global_test_mutex()
79 .lock()
80 .unwrap_or_else(std::sync::PoisonError::into_inner);
81 if let Ok(mut g) = super::installed_lock().write() {
82 g.clear();
83 g.insert("bar".to_string());
84 }
85 assert!(super::is_installed("bar"));
86 assert!(!super::is_installed("baz"));
87 }
88
89 #[cfg(not(target_os = "windows"))]
90 #[allow(clippy::await_holding_lock)]
91 #[tokio::test]
92 async fn refresh_installed_cache_populates_cache_from_pacman_output() {
103 struct PathGuard {
104 original: String,
105 }
106 impl Drop for PathGuard {
107 fn drop(&mut self) {
108 unsafe {
109 std::env::set_var("PATH", &self.original);
110 }
111 }
112 }
113 let _guard = crate::global_test_mutex_lock();
114
115 if let Ok(mut g) = super::installed_lock().write() {
116 g.clear();
117 }
118
119 let original_path = std::env::var("PATH").unwrap_or_default();
120 let _path_guard = PathGuard {
121 original: original_path.clone(),
122 };
123
124 let mut root = std::env::temp_dir();
125 root.push(format!(
126 "pacsea_fake_pacman_qq_{}_{}",
127 std::process::id(),
128 std::time::SystemTime::now()
129 .duration_since(std::time::UNIX_EPOCH)
130 .expect("System time is before UNIX epoch")
131 .as_nanos()
132 ));
133 std::fs::create_dir_all(&root).expect("failed to create test root directory");
134 let mut bin = root.clone();
135 bin.push("bin");
136 std::fs::create_dir_all(&bin).expect("failed to create test bin directory");
137 let mut script = bin.clone();
138 script.push("pacman");
139 let body = r#"#!/usr/bin/env bash
140set -e
141if [[ "$1" == "-Qq" ]]; then
142 echo "alpha"
143 echo "beta"
144 exit 0
145fi
146exit 1
147"#;
148 std::fs::write(&script, body).expect("failed to write test pacman script");
149 #[cfg(unix)]
150 {
151 use std::os::unix::fs::PermissionsExt;
152 let mut perm = std::fs::metadata(&script)
153 .expect("failed to read test pacman script metadata")
154 .permissions();
155 perm.set_mode(0o755);
156 std::fs::set_permissions(&script, perm)
157 .expect("failed to set test pacman script permissions");
158 }
159 let new_path = format!("{}:{original_path}", bin.to_string_lossy());
160 unsafe {
161 std::env::set_var("PATH", &new_path);
162 }
163
164 super::refresh_installed_cache().await;
165
166 let _ = std::fs::remove_dir_all(&root);
167
168 assert!(super::is_installed("alpha"));
169 assert!(super::is_installed("beta"));
170 assert!(!super::is_installed("gamma"));
171 }
172}