diff --git a/src/api2/node/apt.rs b/src/api2/node/apt.rs index d8a2e824..20a738c0 100644 --- a/src/api2/node/apt.rs +++ b/src/api2/node/apt.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use apt_pkg_native::Cache; use anyhow::{Error, bail}; use serde_json::{json, Value}; @@ -71,34 +73,83 @@ fn get_changelog_url( struct FilterData<'a> { // this is version info returned by APT - installed_version: &'a str, + installed_version: Option<&'a str>, candidate_version: &'a str, // this is the version info the filter is supposed to check active_version: &'a str, } -fn list_installed_apt_packages bool>(filter: F) - -> Vec { +enum PackagePreSelect { + OnlyInstalled, + OnlyNew, + All, +} + +fn list_installed_apt_packages bool>( + filter: F, + only_versions_for: Option<&str>, +) -> Vec { let mut ret = Vec::new(); + let mut depends = HashSet::new(); // note: this is not an 'apt update', it just re-reads the cache from disk let mut cache = Cache::get_singleton(); cache.reload(); - let mut cache_iter = cache.iter(); + let mut cache_iter = match only_versions_for { + Some(name) => cache.find_by_name(name), + None => cache.iter() + }; loop { match cache_iter.next() { Some(view) => { - let di = query_detailed_info(&filter, view); + let di = if only_versions_for.is_some() { + query_detailed_info( + PackagePreSelect::All, + &filter, + view, + None + ) + } else { + query_detailed_info( + PackagePreSelect::OnlyInstalled, + &filter, + view, + Some(&mut depends) + ) + }; if let Some(info) = di { ret.push(info); } + + if only_versions_for.is_some() { + break; + } }, None => { + drop(cache_iter); + // also loop through missing dependencies, as they would be installed + for pkg in depends.iter() { + let mut iter = cache.find_by_name(&pkg); + let view = match iter.next() { + Some(view) => view, + None => continue // package not found, ignore + }; + + let di = query_detailed_info( + PackagePreSelect::OnlyNew, + &filter, + view, + None + ); + if let Some(info) = di { + ret.push(info); + } + } break; } } @@ -108,8 +159,10 @@ fn list_installed_apt_packages bool>(filter: F) } fn query_detailed_info<'a, F, V>( + pre_select: PackagePreSelect, filter: F, view: V, + depends: Option<&mut HashSet>, ) -> Option where F: Fn(FilterData) -> bool, @@ -118,11 +171,25 @@ where let current_version = view.current_version(); let candidate_version = view.candidate_version(); - let (current_version, candidate_version) = match (current_version, candidate_version) { - (Some(cur), Some(can)) => (cur, can), // package installed and there is an update - (Some(cur), None) => (cur.clone(), cur), // package installed and up-to-date - (None, Some(_)) => return None, // package could be installed - (None, None) => return None, // broken + let (current_version, candidate_version) = match pre_select { + PackagePreSelect::OnlyInstalled => match (current_version, candidate_version) { + (Some(cur), Some(can)) => (Some(cur), can), // package installed and there is an update + (Some(cur), None) => (Some(cur.clone()), cur), // package installed and up-to-date + (None, Some(_)) => return None, // package could be installed + (None, None) => return None, // broken + }, + PackagePreSelect::OnlyNew => match (current_version, candidate_version) { + (Some(_), Some(_)) => return None, + (Some(_), None) => return None, + (None, Some(can)) => (None, can), + (None, None) => return None, + }, + PackagePreSelect::All => match (current_version, candidate_version) { + (Some(cur), Some(can)) => (Some(cur), can), + (Some(cur), None) => (Some(cur.clone()), cur), + (None, Some(can)) => (None, can), + (None, None) => return None, + }, }; // get additional information via nested APT 'iterators' @@ -139,7 +206,7 @@ where let mut long_desc = "".to_owned(); let fd = FilterData { - installed_version: ¤t_version, + installed_version: current_version.as_deref(), candidate_version: &candidate_version, active_version: &version, }; @@ -192,6 +259,22 @@ where } } + if let Some(depends) = depends { + let mut dep_iter = ver.dep_iter(); + loop { + let dep = match dep_iter.next() { + Some(dep) if dep.dep_type() != "Depends" => continue, + Some(dep) => dep, + None => break + }; + + let dep_pkg = dep.target_pkg(); + let name = dep_pkg.name(); + + depends.insert(name); + } + } + return Some(APTUpdateInfo { package, title: short_desc, @@ -200,7 +283,10 @@ where change_log_url, origin: origin_res, version: candidate_version.clone(), - old_version: current_version.clone(), + old_version: match current_version { + Some(vers) => vers, + None => "".to_owned() + }, priority: priority_res, section: section_res, }); @@ -229,10 +315,10 @@ where )] /// List available APT updates fn apt_update_available(_param: Value) -> Result { - let all_upgradeable = list_installed_apt_packages(|data| + let all_upgradeable = list_installed_apt_packages(|data| { data.candidate_version == data.active_version && - data.installed_version != data.candidate_version - ); + data.installed_version != Some(data.candidate_version) + }, None); Ok(json!(all_upgradeable)) }