pacsea/logic/files/
backup.rs1use super::pkgbuild_cache::{PkgbuildSourceKind, parse_pkgbuild_cached};
4use super::pkgbuild_fetch::{fetch_pkgbuild_sync, fetch_srcinfo_sync};
5use super::pkgbuild_parse::parse_backup_from_srcinfo;
6use crate::state::types::Source;
7use std::process::Command;
8
9pub fn get_backup_files(name: &str, source: &Source) -> Result<Vec<String>, String> {
25 if let Ok(backup_files) = get_backup_files_from_installed(name)
27 && !backup_files.is_empty()
28 {
29 tracing::debug!(
30 "Found {} backup files from installed package {}",
31 backup_files.len(),
32 name
33 );
34 return Ok(backup_files);
35 }
36
37 match source {
39 Source::Official { .. } => {
40 match fetch_pkgbuild_sync(name) {
42 Ok(pkgbuild) => {
43 let entry =
44 parse_pkgbuild_cached(name, None, PkgbuildSourceKind::Official, &pkgbuild);
45 let backup_files = entry.backup_files;
46 if !backup_files.is_empty() {
47 tracing::debug!(
48 "Found {} backup files from PKGBUILD for {}",
49 backup_files.len(),
50 name
51 );
52 return Ok(backup_files);
53 }
54 }
55 Err(e) => {
56 tracing::debug!("Failed to fetch PKGBUILD for {}: {}", name, e);
57 }
58 }
59 Ok(Vec::new())
60 }
61 Source::Aur => {
62 match fetch_srcinfo_sync(name) {
64 Ok(srcinfo) => {
65 let backup_files = parse_backup_from_srcinfo(&srcinfo);
66 if !backup_files.is_empty() {
67 tracing::debug!(
68 "Found {} backup files from .SRCINFO for {}",
69 backup_files.len(),
70 name
71 );
72 return Ok(backup_files);
73 }
74 }
75 Err(e) => {
76 tracing::debug!("Failed to fetch .SRCINFO for {}: {}", name, e);
77 }
78 }
79 match fetch_pkgbuild_sync(name) {
81 Ok(pkgbuild) => {
82 let entry =
83 parse_pkgbuild_cached(name, None, PkgbuildSourceKind::Aur, &pkgbuild);
84 let backup_files = entry.backup_files;
85 if !backup_files.is_empty() {
86 tracing::debug!(
87 "Found {} backup files from PKGBUILD for {}",
88 backup_files.len(),
89 name
90 );
91 return Ok(backup_files);
92 }
93 }
94 Err(e) => {
95 tracing::debug!("Failed to fetch PKGBUILD for {}: {}", name, e);
96 }
97 }
98 Ok(Vec::new())
99 }
100 }
101}
102
103pub fn get_backup_files_from_installed(name: &str) -> Result<Vec<String>, String> {
118 tracing::debug!("Running: pacman -Qii {}", name);
119 let output = Command::new("pacman")
120 .args(["-Qii", name])
121 .env("LC_ALL", "C")
122 .env("LANG", "C")
123 .output()
124 .map_err(|e| {
125 tracing::error!("Failed to execute pacman -Qii {}: {}", name, e);
126 format!("pacman -Qii failed: {e}")
127 })?;
128
129 if !output.status.success() {
130 let stderr = String::from_utf8_lossy(&output.stderr);
132 if stderr.contains("was not found") {
133 tracing::debug!("Package {} is not installed", name);
134 return Ok(Vec::new());
135 }
136 tracing::error!(
137 "pacman -Qii {} failed with status {:?}: {}",
138 name,
139 output.status.code(),
140 stderr
141 );
142 return Err(format!("pacman -Qii failed for {name}: {stderr}"));
143 }
144
145 let text = String::from_utf8_lossy(&output.stdout);
146 let mut backup_files = Vec::new();
147 let mut in_backup_section = false;
148
149 for line in text.lines() {
151 if line.starts_with("Backup Files") {
152 in_backup_section = true;
153 if let Some(colon_pos) = line.find(':') {
155 let files_str = line[colon_pos + 1..].trim();
156 if !files_str.is_empty() && files_str != "None" {
157 for file in files_str.split_whitespace() {
158 backup_files.push(file.to_string());
159 }
160 }
161 }
162 } else if in_backup_section {
163 if line.starts_with(" ") || line.starts_with('\t') {
165 for file in line.split_whitespace() {
166 backup_files.push(file.to_string());
167 }
168 } else {
169 break;
171 }
172 }
173 }
174
175 tracing::debug!(
176 "Found {} backup files for installed package {}",
177 backup_files.len(),
178 name
179 );
180 Ok(backup_files)
181}