#[derive(Debug, Clone, PartialEq)]
pub enum ChangeKind {
Install,
Remove,
Upgrade,
Configure,
}
#[derive(Debug, Clone)]
pub struct PackageChange {
pub kind: ChangeKind,
pub name: String,
pub old_version: Option<String>,
pub new_version: Option<String>,
pub arch: Option<String>,
pub size_bytes: Option<u64>,
}
fn parse_inst(line: &str) -> Option<PackageChange> {
let rest = line.strip_prefix("Inst ")?;
let (name, rest) = rest.split_once(' ').unwrap_or((rest, ""));
let rest = rest.trim();
let (old_ver, rest) = if rest.starts_with('[') {
let end = rest.find(']').unwrap_or(rest.len());
(Some(rest[1..end].to_string()), rest[end + 1..].trim())
} else {
(None, rest)
};
let new_ver = if rest.starts_with('(') {
rest[1..].split_ascii_whitespace().next().map(|s| s.to_string())
} else {
None
};
let kind = if old_ver.is_some() { ChangeKind::Upgrade } else { ChangeKind::Install };
Some(PackageChange {
kind,
name: name.to_string(),
old_version: old_ver,
new_version: new_ver,
arch: None,
size_bytes: None,
})
}
fn parse_remv(line: &str) -> Option<PackageChange> {
let rest = line.strip_prefix("Remv ")?;
let (name, rest) = rest.split_once(' ').unwrap_or((rest, ""));
let old_ver = rest.trim().strip_prefix('[')
.and_then(|s| s.find(']').map(|i| s[..i].to_string()));
Some(PackageChange {
kind: ChangeKind::Remove,
name: name.to_string(),
old_version: old_ver,
new_version: None,
arch: None,
size_bytes: None,
})
}
fn parse_conf(line: &str) -> Option<PackageChange> {
let rest = line.strip_prefix("Conf ")?;
let name = rest.split_ascii_whitespace().next()?.to_string();
Some(PackageChange {
kind: ChangeKind::Configure,
name,
old_version: None,
new_version: None,
arch: None,
size_bytes: None,
})
}
pub fn parse_dry_run_output(output: &str) -> Vec<PackageChange> {
output.lines().filter_map(|line| {
if line.starts_with("Inst ") {
parse_inst(line)
} else if line.starts_with("Remv ") {
parse_remv(line)
} else if line.starts_with("Conf ") {
parse_conf(line)
} else {
None
}
}).collect()
}