use super::command::{CommandError, CommandRunner};
use super::metadata::{
fetch_installed_size, fetch_installed_version, parse_pacman_key_values, parse_size_to_bytes,
};
use crate::state::types::PackageItem;
pub(super) fn batch_fetch_installed_versions<R: CommandRunner>(
runner: &R,
items: &[PackageItem],
) -> Vec<Result<String, CommandError>> {
const BATCH_SIZE: usize = 50;
let mut results = Vec::with_capacity(items.len());
for chunk in items.chunks(BATCH_SIZE) {
let names: Vec<&str> = chunk.iter().map(|i| i.name.as_str()).collect();
let mut args = vec!["-Q"];
args.extend(names.iter().copied());
match runner.run("pacman", &args) {
Ok(output) => {
let mut version_map = std::collections::HashMap::new();
for line in output.lines() {
let mut parts = line.split_whitespace();
if let (Some(name), Some(version)) = (parts.next(), parts.next_back()) {
version_map.insert(name, version.to_string());
}
}
for item in chunk {
if let Some(version) = version_map.get(item.name.as_str()) {
results.push(Ok(version.clone()));
} else {
results.push(Err(CommandError::Parse {
program: "pacman -Q".to_string(),
field: format!("version for {}", item.name),
}));
}
}
}
Err(_) => {
for item in chunk {
match fetch_installed_version(runner, &item.name) {
Ok(v) => results.push(Ok(v)),
Err(err) => results.push(Err(err)),
}
}
}
}
}
results
}
pub(super) fn batch_fetch_installed_sizes<R: CommandRunner>(
runner: &R,
items: &[PackageItem],
) -> Vec<Result<u64, CommandError>> {
const BATCH_SIZE: usize = 50;
let mut results = Vec::with_capacity(items.len());
for chunk in items.chunks(BATCH_SIZE) {
let names: Vec<&str> = chunk.iter().map(|i| i.name.as_str()).collect();
let mut args = vec!["-Qi"];
args.extend(names.iter().copied());
match runner.run("pacman", &args) {
Ok(output) => {
let mut package_blocks = Vec::new();
let mut current_block = String::new();
for line in output.lines() {
if line.trim().is_empty() {
if !current_block.is_empty() {
package_blocks.push(current_block.clone());
current_block.clear();
}
} else {
current_block.push_str(line);
current_block.push('\n');
}
}
if !current_block.is_empty() {
package_blocks.push(current_block);
}
let mut size_map = std::collections::HashMap::new();
for block in package_blocks {
let block_fields = parse_pacman_key_values(&block);
if let (Some(name), Some(size_str)) = (
block_fields.get("Name").map(|s| s.trim()),
block_fields.get("Installed Size").map(|s| s.trim()),
) && let Some(size_bytes) = parse_size_to_bytes(size_str)
{
size_map.insert(name.to_string(), size_bytes);
}
}
for item in chunk {
if let Some(size) = size_map.get(&item.name) {
results.push(Ok(*size));
} else {
results.push(Err(CommandError::Parse {
program: "pacman -Qi".to_string(),
field: format!("Installed Size for {}", item.name),
}));
}
}
}
Err(_) => {
for item in chunk {
match fetch_installed_size(runner, &item.name) {
Ok(s) => results.push(Ok(s)),
Err(err) => results.push(Err(err)),
}
}
}
}
}
results
}