pacsea/logic/files/
lists.rs1use super::pkgbuild_cache::{PkgbuildSourceKind, parse_pkgbuild_cached};
4use super::pkgbuild_fetch::fetch_pkgbuild_sync;
5use crate::state::types::Source;
6use std::process::Command;
7
8fn parse_file_list_from_output(output: &[u8]) -> Vec<String> {
19 let text = String::from_utf8_lossy(output);
20 text.lines()
21 .filter_map(|line| line.split_once(' ').map(|(_pkg, path)| path.to_string()))
22 .collect()
23}
24
25fn try_aur_helper_file_list(helper: &str, name: &str) -> Option<Vec<String>> {
38 tracing::debug!("Trying {} -Fl {} for AUR package file list", helper, name);
39 let output = Command::new(helper)
40 .args(["-Fl", name])
41 .env("LC_ALL", "C")
42 .env("LANG", "C")
43 .output()
44 .ok()?;
45
46 if !output.status.success() {
47 return None;
48 }
49
50 let files = parse_file_list_from_output(&output.stdout);
51 if files.is_empty() {
52 return None;
53 }
54
55 tracing::debug!(
56 "Found {} files from {} -Fl for {}",
57 files.len(),
58 helper,
59 name
60 );
61 Some(files)
62}
63
64fn get_aur_file_list(name: &str) -> Vec<String> {
75 if let Ok(installed_files) = get_installed_file_list(name)
77 && !installed_files.is_empty()
78 {
79 tracing::debug!(
80 "Found {} files from installed AUR package {}",
81 installed_files.len(),
82 name
83 );
84 return installed_files;
85 }
86
87 let has_paru = Command::new("paru").args(["--version"]).output().is_ok();
89 let has_yay = Command::new("yay").args(["--version"]).output().is_ok();
90
91 if has_paru && let Some(files) = try_aur_helper_file_list("paru", name) {
92 return files;
93 }
94
95 if has_yay && let Some(files) = try_aur_helper_file_list("yay", name) {
96 return files;
97 }
98
99 if let Ok(pkgbuild) = fetch_pkgbuild_sync(name) {
101 let entry = parse_pkgbuild_cached(name, None, PkgbuildSourceKind::Aur, &pkgbuild);
102 let files = entry.install_paths;
103 if !files.is_empty() {
104 tracing::debug!(
105 "Found {} files from PKGBUILD parsing for {}",
106 files.len(),
107 name
108 );
109 return files;
110 }
111 } else {
112 tracing::debug!("Failed to fetch PKGBUILD for {}", name);
113 }
114
115 tracing::debug!(
117 "AUR package {}: file list not available (not installed, not cached, PKGBUILD parsing failed)",
118 name
119 );
120 Vec::new()
121}
122
123fn get_official_file_list(name: &str, repo: &str) -> Result<Vec<String>, String> {
135 tracing::debug!("Running: pacman -Fl {}", name);
136 let spec = if repo.is_empty() {
137 name.to_string()
138 } else {
139 format!("{repo}/{name}")
140 };
141
142 let output = Command::new("pacman")
143 .args(["-Fl", &spec])
144 .env("LC_ALL", "C")
145 .env("LANG", "C")
146 .output()
147 .map_err(|e| {
148 tracing::error!("Failed to execute pacman -Fl {}: {}", spec, e);
149 format!("pacman -Fl failed: {e}")
150 })?;
151
152 if !output.status.success() {
153 let stderr = String::from_utf8_lossy(&output.stderr);
154 if stderr.contains("database file") && stderr.contains("does not exist") {
156 tracing::warn!(
157 "File database not synced for {} (pacman -Fy requires root). Skipping file list.",
158 name
159 );
160 return Ok(Vec::new()); }
162 tracing::error!(
163 "pacman -Fl {} failed with status {:?}: {}",
164 spec,
165 output.status.code(),
166 stderr
167 );
168 return Err(format!("pacman -Fl failed for {spec}: {stderr}"));
169 }
170
171 let files = parse_file_list_from_output(&output.stdout);
172 tracing::debug!("Found {} files in remote package {}", files.len(), name);
173 Ok(files)
174}
175
176pub fn get_remote_file_list(name: &str, source: &Source) -> Result<Vec<String>, String> {
192 match source {
193 Source::Official { repo, .. } => get_official_file_list(name, repo),
194 Source::Aur => Ok(get_aur_file_list(name)),
195 }
196}
197
198pub fn get_installed_file_list(name: &str) -> Result<Vec<String>, String> {
213 tracing::debug!("Running: pacman -Ql {}", name);
214 let output = Command::new("pacman")
215 .args(["-Ql", name])
216 .env("LC_ALL", "C")
217 .env("LANG", "C")
218 .output()
219 .map_err(|e| {
220 tracing::error!("Failed to execute pacman -Ql {}: {}", name, e);
221 format!("pacman -Ql failed: {e}")
222 })?;
223
224 if !output.status.success() {
225 let stderr = String::from_utf8_lossy(&output.stderr);
227 if stderr.contains("was not found") {
228 tracing::debug!("Package {} is not installed", name);
229 return Ok(Vec::new());
230 }
231 tracing::error!(
232 "pacman -Ql {} failed with status {:?}: {}",
233 name,
234 output.status.code(),
235 stderr
236 );
237 return Err(format!("pacman -Ql failed for {name}: {stderr}"));
238 }
239
240 let files = parse_file_list_from_output(&output.stdout);
241 tracing::debug!("Found {} files in installed package {}", files.len(), name);
242 Ok(files)
243}