use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;
use std::rc::Rc;
use cxx::UniquePtr;
use once_cell::unsync::OnceCell;
use crate::cache::raw::{
pkg_cache_find_name, pkg_list, pkg_version_list, ver_file_list, ver_pkg_file_list,
};
use crate::cache::{Cache, PackageFile, PackageSort, PointerMap};
use crate::depcache::DepCache;
use crate::records::Records;
use crate::resolver::ProblemResolver;
use crate::util::{cmp_versions, unit_str, NumSys};
#[derive(Debug)]
pub enum Mark {
Keep,
Auto,
Manual,
Remove,
Purge,
Install,
Reinstall,
NoReinstall,
Downgrade,
Upgrade,
}
#[derive(Debug)]
pub struct Package<'a> {
_lifetime: &'a PhantomData<Cache>,
records: Rc<RefCell<Records>>,
cache_ptr: Rc<RefCell<UniquePtr<raw::PkgCacheFile>>>,
depcache: Rc<RefCell<DepCache>>,
resolver: Rc<RefCell<ProblemResolver>>,
pointer_map: Rc<RefCell<PointerMap>>,
pub(crate) ptr: Rc<RefCell<raw::PackagePtr>>,
}
impl<'a> Package<'a> {
pub(crate) fn new(
records: Rc<RefCell<Records>>,
cache_ptr: Rc<RefCell<UniquePtr<raw::PkgCacheFile>>>,
depcache: Rc<RefCell<DepCache>>,
resolver: Rc<RefCell<ProblemResolver>>,
pointer_map: Rc<RefCell<PointerMap>>,
ptr: Rc<RefCell<raw::PackagePtr>>,
) -> Package<'a> {
Package {
_lifetime: &PhantomData,
records,
cache_ptr,
depcache,
resolver,
pointer_map,
ptr,
}
}
pub(crate) fn make_package(&self, new_ptr: raw::PackagePtr) -> Package {
Package::new(
Rc::clone(&self.records),
Rc::clone(&self.cache_ptr),
Rc::clone(&self.depcache),
Rc::clone(&self.resolver),
Rc::clone(&self.pointer_map),
Rc::clone(&self.pointer_map.borrow_mut().get_package(new_ptr)),
)
}
pub fn fullname(&self, pretty: bool) -> String { raw::get_fullname(&self.ptr.borrow(), pretty) }
pub fn name(&self) -> String { raw::pkg_name(&self.ptr.borrow()) }
pub fn arch(&self) -> String { raw::pkg_arch(&self.ptr.borrow()) }
pub fn id(&self) -> u32 { raw::pkg_id(&self.ptr.borrow()) }
pub fn current_state(&self) -> u8 { raw::pkg_current_state(&self.ptr.borrow()) }
pub fn inst_state(&self) -> u8 { raw::pkg_inst_state(&self.ptr.borrow()) }
pub fn selected_state(&self) -> u8 { raw::pkg_selected_state(&self.ptr.borrow()) }
pub fn essential(&self) -> bool { raw::pkg_essential(&self.ptr.borrow()) }
pub fn has_versions(&self) -> bool { raw::pkg_has_versions(&self.ptr.borrow()) }
pub fn has_provides(&self) -> bool { raw::pkg_has_provides(&self.ptr.borrow()) }
pub fn rev_provides_list(&self, version_rel: Option<(&str, &str)>) -> Vec<Version> {
if let Some((operator, _)) = version_rel {
if !vec!["<<", "<=", "=", ">=", ">>"].contains(&operator) {
panic!("Invalid operator `{}`.", operator);
}
}
let mut returned_pkgs = Vec::new();
let master_pkgname = self.name();
for pkg_ptr in pkg_list(&self.cache_ptr.borrow(), &PackageSort::default()) {
let pkg = self.make_package(pkg_ptr);
let pkg_pkgname = pkg.fullname(true);
for version in pkg.versions() {
let provides_pkgs = version.provides_list();
for (provides_pkgname, provides_version) in provides_pkgs {
#[allow(clippy::if_same_then_else)]
if provides_pkgname != master_pkgname {
continue;
} else if version_rel.is_some() && provides_version.is_none() {
continue;
} else if version_rel.is_none() {
let ret_ptr =
pkg_cache_find_name(&self.cache_ptr.borrow(), pkg_pkgname.to_owned())
.unwrap();
returned_pkgs.push(
self.make_package(ret_ptr)
.get_version(&version.version())
.unwrap(),
);
continue;
}
let (version_operator, ver_version) = version_rel.unwrap();
let ver_cmp_result =
cmp_versions(ver_version, provides_version.as_ref().unwrap());
let good_version = match version_operator {
"<<" => ver_cmp_result == Ordering::Less,
"<=" => vec![Ordering::Less, Ordering::Equal].contains(&ver_cmp_result),
"=" => ver_cmp_result == Ordering::Equal,
">=" => vec![Ordering::Equal, Ordering::Greater].contains(&ver_cmp_result),
">>" => ver_cmp_result == Ordering::Greater,
_ => unreachable!(),
};
if good_version {
let ret_ptr =
pkg_cache_find_name(&self.cache_ptr.borrow(), pkg_pkgname.to_owned())
.unwrap();
returned_pkgs.push(
self.make_package(ret_ptr)
.get_version(&version.version())
.unwrap(),
);
}
}
}
}
returned_pkgs
}
fn create_version(&self, ver: raw::VersionPtr) -> Version<'a> {
Version::new(
Rc::clone(&self.records),
Rc::clone(&self.cache_ptr),
Rc::clone(&self.depcache),
Rc::clone(&self.resolver),
Rc::clone(&self.pointer_map),
Rc::clone(&self.ptr),
self.pointer_map.borrow_mut().get_version(ver),
)
}
pub fn candidate(&self) -> Option<Version<'a>> {
let ver =
raw::pkg_candidate_version(&self.records.borrow().cache.borrow(), &self.ptr.borrow());
if ver.ptr.is_null() {
return None;
}
Some(self.create_version(ver))
}
pub fn installed(&self) -> Option<Version<'a>> {
let ver = raw::pkg_current_version(&self.ptr.borrow());
if ver.ptr.is_null() {
return None;
}
Some(self.create_version(ver))
}
pub fn get_version(&self, version_str: &str) -> Option<Version<'a>> {
Some(self.create_version(
raw::pkg_get_version(&self.ptr.borrow(), version_str.to_string()).ok()?,
))
}
pub fn is_installed(&self) -> bool { raw::pkg_is_installed(&self.ptr.borrow()) }
pub fn is_upgradable(&self, skip_depcache: bool) -> bool {
self.depcache
.borrow()
.is_upgradable(&self.ptr.borrow(), skip_depcache)
}
pub fn is_auto_installed(&self) -> bool {
self.depcache.borrow().is_auto_installed(&self.ptr.borrow())
}
pub fn is_auto_removable(&self) -> bool {
self.depcache.borrow().is_auto_removable(&self.ptr.borrow())
}
pub fn is_now_broken(&self) -> bool { self.depcache.borrow().is_now_broken(&self.ptr.borrow()) }
pub fn is_inst_broken(&self) -> bool {
self.depcache.borrow().is_inst_broken(&self.ptr.borrow())
}
pub fn marked_install(&self) -> bool {
self.depcache.borrow().marked_install(&self.ptr.borrow())
}
pub fn marked_upgrade(&self) -> bool {
self.depcache.borrow().marked_upgrade(&self.ptr.borrow())
}
pub fn marked_purge(&self) -> bool { self.depcache.borrow().marked_purge(&self.ptr.borrow()) }
pub fn marked_delete(&self) -> bool { self.depcache.borrow().marked_delete(&self.ptr.borrow()) }
pub fn marked_keep(&self) -> bool { self.depcache.borrow().marked_keep(&self.ptr.borrow()) }
pub fn marked_downgrade(&self) -> bool {
self.depcache.borrow().marked_downgrade(&self.ptr.borrow())
}
pub fn marked_reinstall(&self) -> bool {
self.depcache.borrow().marked_reinstall(&self.ptr.borrow())
}
pub fn state(&self, mark: &Mark) -> bool {
match mark {
Mark::Keep => self.marked_keep(),
Mark::Auto => self.is_auto_installed(),
Mark::Manual => !self.is_auto_installed(),
Mark::Remove => self.marked_delete(),
Mark::Purge => self.marked_purge(),
Mark::Install => self.marked_install(),
Mark::Reinstall => self.marked_reinstall(),
Mark::NoReinstall => !self.marked_reinstall(),
Mark::Upgrade => self.marked_upgrade(),
Mark::Downgrade => self.marked_downgrade(),
}
}
pub fn set(&self, mark: &Mark) -> bool {
match mark {
Mark::Keep => self.mark_keep(),
Mark::Auto => self.mark_auto(true),
Mark::Manual => self.mark_auto(false),
Mark::Remove => self.mark_delete(false),
Mark::Purge => self.mark_delete(true),
Mark::Install => self.mark_install(true, true),
Mark::Reinstall => self.mark_reinstall(true),
Mark::NoReinstall => self.mark_reinstall(false),
Mark::Upgrade => self.mark_install(true, true),
Mark::Downgrade => unimplemented!(),
}
}
pub fn mark_auto(&self, mark_auto: bool) -> bool {
self.depcache
.borrow()
.mark_auto(&self.ptr.borrow(), mark_auto);
true
}
pub fn mark_keep(&self) -> bool { self.depcache.borrow().mark_keep(&self.ptr.borrow()) }
pub fn mark_delete(&self, purge: bool) -> bool {
self.depcache
.borrow()
.mark_delete(&self.ptr.borrow(), purge)
}
pub fn mark_install(&self, auto_inst: bool, from_user: bool) -> bool {
self.depcache
.borrow()
.mark_install(&self.ptr.borrow(), auto_inst, from_user)
}
pub fn mark_reinstall(&self, reinstall: bool) -> bool {
self.depcache
.borrow()
.mark_reinstall(&self.ptr.borrow(), reinstall);
true
}
pub fn protect(&self) { self.resolver.borrow().protect(&self.ptr.borrow()) }
pub fn versions(&self) -> impl Iterator<Item = Version<'a>> + '_ {
pkg_version_list(&self.ptr.borrow()).into_iter().map(|ver| {
Version::new(
Rc::clone(&self.records),
Rc::clone(&self.cache_ptr),
Rc::clone(&self.depcache),
Rc::clone(&self.resolver),
Rc::clone(&self.pointer_map),
Rc::clone(&self.ptr),
self.pointer_map.borrow_mut().get_version(ver),
)
})
}
}
impl<'a> fmt::Display for Package<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Package< name: {}, arch: {}, id: {}, essential: {}, states: [curr: {}, inst {}, sel \
{}], virtual: {}, provides: {}>",
self.name(),
self.arch(),
self.id(),
self.essential(),
self.current_state(),
self.inst_state(),
self.selected_state(),
!self.has_versions(),
self.has_provides(),
)?;
Ok(())
}
}
impl<'a> PartialEq for Package<'a> {
fn eq(&self, other: &Self) -> bool { self.id() == other.id() }
}
#[derive(Debug)]
pub struct Version<'a> {
_lifetime: &'a PhantomData<Cache>,
records: Rc<RefCell<Records>>,
cache_ptr: Rc<RefCell<UniquePtr<raw::PkgCacheFile>>>,
depcache: Rc<RefCell<DepCache>>,
resolver: Rc<RefCell<ProblemResolver>>,
pointer_map: Rc<RefCell<PointerMap>>,
parent: Rc<RefCell<raw::PackagePtr>>,
ptr: Rc<RefCell<raw::VersionPtr>>,
depends_list: OnceCell<HashMap<String, Vec<Dependency>>>,
}
impl<'a> Version<'a> {
fn new(
records: Rc<RefCell<Records>>,
cache_ptr: Rc<RefCell<UniquePtr<raw::PkgCacheFile>>>,
depcache: Rc<RefCell<DepCache>>,
resolver: Rc<RefCell<ProblemResolver>>,
pointer_map: Rc<RefCell<PointerMap>>,
parent: Rc<RefCell<raw::PackagePtr>>,
ptr: Rc<RefCell<raw::VersionPtr>>,
) -> Self {
Self {
_lifetime: &PhantomData,
records,
cache_ptr,
depcache,
resolver,
pointer_map,
parent,
ptr,
depends_list: OnceCell::new(),
}
}
pub fn parent(&self) -> Package {
Package::new(
Rc::clone(&self.records),
Rc::clone(&self.cache_ptr),
Rc::clone(&self.depcache),
Rc::clone(&self.resolver),
Rc::clone(&self.pointer_map),
Rc::clone(&self.parent),
)
}
pub fn arch(&self) -> String { raw::ver_arch(&self.ptr.borrow()) }
pub fn version(&self) -> String { raw::ver_str(&self.ptr.borrow()) }
pub fn section(&self) -> String { raw::ver_section(&self.ptr.borrow()) }
pub fn provides_list(&self) -> Vec<(String, Option<String>)> {
let mut returned_pkgs = Vec::new();
for pkg in raw::ver_provides_list(&self.ptr.borrow()) {
let (pkgname, ver) = pkg.split_once('/').unwrap();
if !ver.is_empty() {
returned_pkgs.push((pkgname.to_string(), Some(ver.to_string())));
} else {
returned_pkgs.push((pkgname.to_string(), None));
}
}
returned_pkgs
}
pub fn priority_str(&self) -> String { raw::ver_priority_str(&self.ptr.borrow()) }
pub fn source_name(&self) -> String { raw::ver_source_name(&self.ptr.borrow()) }
pub fn source_version(&self) -> String { raw::ver_source_version(&self.ptr.borrow()) }
pub fn priority(&self) -> i32 {
raw::ver_priority(&self.records.borrow().cache.borrow(), &self.ptr.borrow())
}
pub fn size(&self) -> u64 { raw::ver_size(&self.ptr.borrow()) }
pub fn installed_size(&self) -> u64 { raw::ver_installed_size(&self.ptr.borrow()) }
pub fn id(&self) -> u32 { raw::ver_id(&self.ptr.borrow()) }
pub fn downloadable(&self) -> bool { raw::ver_downloadable(&self.ptr.borrow()) }
pub fn is_installed(&self) -> bool { raw::ver_installed(&self.ptr.borrow()) }
pub fn set_candidate(&self) { self.depcache.borrow().set_candidate(&self.ptr.borrow()); }
pub fn depends_map(&self) -> &HashMap<String, Vec<Dependency>> {
self.depends_list.get_or_init(|| self.gen_depends())
}
pub fn get_depends(&self, key: &str) -> Option<&Vec<Dependency>> {
self.depends_list
.get_or_init(|| self.gen_depends())
.get(key)
}
pub fn enhances(&self) -> Option<&Vec<Dependency>> { self.get_depends("Enhances") }
pub fn dependencies(&self) -> Option<Vec<&Dependency>> {
let mut ret_vec: Vec<&Dependency> = Vec::new();
if let Some(dep_list) = self.get_depends("Depends") {
for dep in dep_list {
ret_vec.push(dep)
}
}
if let Some(dep_list) = self.get_depends("PreDepends") {
for dep in dep_list {
ret_vec.push(dep)
}
}
if ret_vec.is_empty() {
return None;
}
Some(ret_vec)
}
pub fn recommends(&self) -> Option<&Vec<Dependency>> { self.get_depends("Recommends") }
pub fn suggests(&self) -> Option<&Vec<Dependency>> { self.get_depends("Suggests") }
pub fn description(&self) -> String {
let mut records = self.records.borrow_mut();
records.lookup_desc(&self.ptr.borrow().desc);
records.description()
}
pub fn summary(&self) -> String {
let mut records = self.records.borrow_mut();
records.lookup_desc(&self.ptr.borrow().desc);
records.summary()
}
pub fn sha256(&self) -> Option<String> { self.hash("sha256") }
pub fn sha512(&self) -> Option<String> { self.hash("sha512") }
pub fn hash(&self, hash_type: &str) -> Option<String> {
let ver_file = ver_file_list(&self.ptr.borrow()).into_iter().next()?;
let mut records = self.records.borrow_mut();
records.lookup_ver(&ver_file);
records.hash_find(hash_type)
}
pub fn package_files(&self) -> impl Iterator<Item = PackageFile> + '_ {
ver_pkg_file_list(&self.ptr.borrow())
.into_iter()
.map(|pkg_file| PackageFile::new(pkg_file, Rc::clone(&self.records.borrow().cache)))
}
pub fn uris(&self) -> impl Iterator<Item = String> + '_ {
ver_file_list(&self.ptr.borrow())
.into_iter()
.filter_map(|ver_file| {
let mut records = self.records.borrow_mut();
records.lookup_ver(&ver_file);
let uri = records.uri(&ver_file);
if !uri.starts_with("file:") {
Some(uri)
} else {
None
}
})
}
fn convert_depends(&self, raw_deps: raw::DepContainer) -> Dependency {
let mut base_vec = Vec::new();
for base_dep in raw_deps.dep_list {
base_vec.push(BaseDep {
records: Rc::clone(&self.records),
cache_ptr: Rc::clone(&self.cache_ptr),
depcache: Rc::clone(&self.depcache),
resolver: Rc::clone(&self.resolver),
pointer_map: Rc::clone(&self.pointer_map),
package: Rc::clone(&self.parent),
apt_dep: base_dep,
})
}
Dependency {
dep_type: raw_deps.dep_type,
base_deps: base_vec,
}
}
fn gen_depends(&self) -> HashMap<String, Vec<Dependency>> {
let mut dependencies: HashMap<String, Vec<Dependency>> = HashMap::new();
for dep in raw::dep_list(&self.ptr.borrow()) {
if let Some(vec) = dependencies.get_mut(&dep.dep_type) {
vec.push(self.convert_depends(dep))
} else {
dependencies.insert(dep.dep_type.to_owned(), vec![self.convert_depends(dep)]);
}
}
dependencies
}
}
impl<'a> fmt::Display for Version<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}: Version {} <ID: {}, arch: {}, size: {}, installed_size: {}, section: {} Priority \
{} at {}, downloadable: {}>",
self.parent().name(),
self.version(),
self.id(),
self.arch(),
unit_str(self.size(), NumSys::Decimal),
unit_str(self.installed_size(), NumSys::Decimal),
self.section(),
self.priority_str(),
self.priority(),
self.downloadable(),
)?;
Ok(())
}
}
impl<'a> PartialEq for Version<'a> {
fn eq(&self, other: &Self) -> bool {
matches!(
cmp_versions(&self.version(), &other.version()),
Ordering::Equal
)
}
}
impl<'a> PartialOrd for Version<'a> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(cmp_versions(&self.version(), &other.version()))
}
}
#[derive(Debug)]
pub struct BaseDep {
apt_dep: raw::BaseDep,
records: Rc<RefCell<Records>>,
cache_ptr: Rc<RefCell<UniquePtr<raw::PkgCacheFile>>>,
depcache: Rc<RefCell<DepCache>>,
resolver: Rc<RefCell<ProblemResolver>>,
pointer_map: Rc<RefCell<PointerMap>>,
package: Rc<RefCell<raw::PackagePtr>>,
}
impl BaseDep {
pub fn name(&self) -> &String { &self.apt_dep.name }
pub fn version(&self) -> &String { &self.apt_dep.version }
pub fn comp(&self) -> &String { &self.apt_dep.comp }
pub fn dep_type(&self) -> &String { &self.apt_dep.dep_type }
pub fn all_targets(&self) -> impl Iterator<Item = Version> {
raw::dep_all_targets(&self.apt_dep).into_iter().map(|ptr| {
Version::new(
Rc::clone(&self.records),
Rc::clone(&self.cache_ptr),
Rc::clone(&self.depcache),
Rc::clone(&self.resolver),
Rc::clone(&self.pointer_map),
Rc::clone(&self.package),
self.pointer_map.borrow_mut().get_version(ptr),
)
})
}
}
impl fmt::Display for BaseDep {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"BaseDep <Name: {}, Version: {}, Comp: {}, Type: {}>",
self.name(),
self.version(),
self.comp(),
self.dep_type(),
)?;
Ok(())
}
}
#[derive(Debug)]
pub struct Dependency {
pub dep_type: String,
pub base_deps: Vec<BaseDep>,
}
impl Dependency {
pub fn is_or(&self) -> bool { self.base_deps.len() > 1 }
pub fn first(&self) -> &BaseDep { &self.base_deps[0] }
}
impl fmt::Display for Dependency {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_or() {
write!(f, "Or Dependencies[")?;
} else {
write!(f, "Dependency[")?;
}
for dep in &self.base_deps {
write!(
f,
"\n BaseDep <Name: {}, Version: {}, Comp: {}, Type: {}>,",
dep.name(),
dep.version(),
dep.comp(),
dep.dep_type(),
)?;
}
write!(f, "\n]")?;
Ok(())
}
}
#[cxx::bridge]
pub mod raw {
struct BaseDep {
name: String,
version: String,
comp: String,
dep_type: String,
ptr: SharedPtr<DepIterator>,
}
struct DepContainer {
dep_type: String,
dep_list: Vec<BaseDep>,
}
unsafe extern "C++" {
type DepIterator;
type PkgCacheFile = crate::cache::raw::PkgCacheFile;
type VersionPtr = crate::cache::raw::VersionPtr;
type PackagePtr = crate::cache::raw::PackagePtr;
type VersionFile = crate::cache::raw::VersionFile;
include!("rust-apt/apt-pkg-c/cache.h");
include!("rust-apt/apt-pkg-c/package.h");
pub fn pkg_current_version(iterator: &PackagePtr) -> VersionPtr;
pub fn pkg_candidate_version(
cache: &UniquePtr<PkgCacheFile>,
iterator: &PackagePtr,
) -> VersionPtr;
pub fn pkg_get_version(iterator: &PackagePtr, version_str: String) -> Result<VersionPtr>;
pub fn pkg_is_installed(iterator: &PackagePtr) -> bool;
pub fn pkg_has_versions(iterator: &PackagePtr) -> bool;
pub fn pkg_has_provides(iterator: &PackagePtr) -> bool;
pub fn pkg_essential(iterator: &PackagePtr) -> bool;
pub fn get_fullname(iterator: &PackagePtr, pretty: bool) -> String;
pub fn pkg_name(pkg: &PackagePtr) -> String;
pub fn pkg_arch(iterator: &PackagePtr) -> String;
pub fn pkg_id(iterator: &PackagePtr) -> u32;
pub fn pkg_current_state(iterator: &PackagePtr) -> u8;
pub fn pkg_inst_state(iterator: &PackagePtr) -> u8;
pub fn pkg_selected_state(iterator: &PackagePtr) -> u8;
pub fn dep_list(version: &VersionPtr) -> Vec<DepContainer>;
pub fn ver_parent(version: &VersionPtr) -> PackagePtr;
pub fn ver_arch(version: &VersionPtr) -> String;
pub fn ver_provides_list(version: &VersionPtr) -> Vec<String>;
pub fn ver_str(version: &VersionPtr) -> String;
pub fn ver_section(version: &VersionPtr) -> String;
pub fn ver_priority_str(version: &VersionPtr) -> String;
pub fn ver_source_name(version: &VersionPtr) -> String;
pub fn ver_source_version(version: &VersionPtr) -> String;
pub fn ver_priority(cache: &UniquePtr<PkgCacheFile>, version: &VersionPtr) -> i32;
pub fn ver_size(version: &VersionPtr) -> u64;
pub fn ver_installed_size(version: &VersionPtr) -> u64;
pub fn ver_id(version: &VersionPtr) -> u32;
pub fn ver_downloadable(version: &VersionPtr) -> bool;
pub fn ver_installed(version: &VersionPtr) -> bool;
pub fn dep_all_targets(dep: &BaseDep) -> Vec<VersionPtr>;
}
}
impl fmt::Debug for raw::BaseDep {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"BaseDep <Name: {}, Version: {}, Comp: {}, Type: {}>",
self.name, self.version, self.comp, self.dep_type,
)?;
Ok(())
}
}