pacsea/logic/files/
mod.rs1mod backup;
4mod db_sync;
5mod lists;
6mod pkgbuild_cache;
7mod pkgbuild_fetch;
8mod pkgbuild_parse;
9mod resolution;
10
11pub use backup::{get_backup_files, get_backup_files_from_installed};
12pub use db_sync::{
13 ensure_file_db_synced, get_file_db_sync_info, get_file_db_sync_timestamp, is_file_db_stale,
14};
15pub use lists::{get_installed_file_list, get_remote_file_list};
16pub use pkgbuild_cache::{PkgbuildSourceKind, flush_pkgbuild_cache, parse_pkgbuild_cached};
17pub use pkgbuild_fetch::{fetch_pkgbuild_sync, fetch_srcinfo_sync, get_pkgbuild_from_cache};
18pub use pkgbuild_parse::{
19 parse_backup_array_content, parse_backup_from_pkgbuild, parse_backup_from_srcinfo,
20 parse_install_paths_from_pkgbuild,
21};
22pub use resolution::{
23 batch_get_remote_file_lists, resolve_install_files, resolve_install_files_with_remote_list,
24 resolve_package_files, resolve_remove_files,
25};
26
27use crate::state::modal::PackageFileInfo;
28use crate::state::types::PackageItem;
29
30#[allow(clippy::missing_const_for_fn)]
42pub fn resolve_file_changes(
43 items: &[PackageItem],
44 action: crate::state::modal::PreflightAction,
45) -> Vec<PackageFileInfo> {
46 const MAX_AUTO_SYNC_AGE_DAYS: u64 = 30;
49 let _span = tracing::info_span!(
50 "resolve_file_changes",
51 stage = "files",
52 item_count = items.len()
53 )
54 .entered();
55 let start_time = std::time::Instant::now();
56
57 if items.is_empty() {
58 tracing::warn!("No packages provided for file resolution");
59 return Vec::new();
60 }
61 match ensure_file_db_synced(false, MAX_AUTO_SYNC_AGE_DAYS) {
62 Ok(synced) => {
63 if synced {
64 tracing::info!("File database was synced automatically (was very stale)");
65 } else {
66 tracing::debug!("File database is fresh, no sync needed");
67 }
68 }
69 Err(e) => {
70 tracing::warn!("File database sync failed: {} (continuing without sync)", e);
72 }
73 }
74
75 let official_packages: Vec<(&str, &crate::state::types::Source)> = items
77 .iter()
78 .filter_map(|item| {
79 if matches!(item.source, crate::state::types::Source::Official { .. }) {
80 Some((item.name.as_str(), &item.source))
81 } else {
82 None
83 }
84 })
85 .collect();
86 let batched_remote_files_cache = if !official_packages.is_empty()
87 && matches!(action, crate::state::modal::PreflightAction::Install)
88 {
89 resolution::batch_get_remote_file_lists(&official_packages)
90 } else {
91 std::collections::HashMap::new()
92 };
93
94 let mut results = Vec::new();
95
96 for (idx, item) in items.iter().enumerate() {
97 tracing::info!(
98 "[{}/{}] Resolving files for package: {} ({:?})",
99 idx + 1,
100 items.len(),
101 item.name,
102 item.source
103 );
104
105 let use_batched = matches!(action, crate::state::modal::PreflightAction::Install)
107 && matches!(item.source, crate::state::types::Source::Official { .. })
108 && batched_remote_files_cache.contains_key(item.name.as_str());
109
110 match if use_batched {
111 let remote_files = batched_remote_files_cache
113 .get(item.name.as_str())
114 .cloned()
115 .unwrap_or_default();
116 resolution::resolve_install_files_with_remote_list(
117 &item.name,
118 &item.source,
119 remote_files,
120 )
121 } else {
122 resolution::resolve_package_files(&item.name, &item.source, action)
123 } {
124 Ok(file_info) => {
125 tracing::info!(
126 " Found {} files for {} ({} new, {} changed, {} removed)",
127 file_info.total_count,
128 item.name,
129 file_info.new_count,
130 file_info.changed_count,
131 file_info.removed_count
132 );
133 results.push(file_info);
134 }
135 Err(e) => {
136 tracing::warn!(" Failed to resolve files for {}: {}", item.name, e);
137 results.push(PackageFileInfo {
139 name: item.name.clone(),
140 files: Vec::new(),
141 total_count: 0,
142 new_count: 0,
143 changed_count: 0,
144 removed_count: 0,
145 config_count: 0,
146 pacnew_candidates: 0,
147 pacsave_candidates: 0,
148 });
149 }
150 }
151 }
152
153 let elapsed = start_time.elapsed();
154 let duration_ms = u64::try_from(elapsed.as_millis()).unwrap_or(u64::MAX);
155 tracing::info!(
156 stage = "files",
157 item_count = items.len(),
158 result_count = results.len(),
159 duration_ms = duration_ms,
160 "File resolution complete"
161 );
162 results
163}
164
165#[cfg(all(test, unix))]
166mod tests;