diff --git a/alpm/src/add.rs b/alpm/src/add.rs index 70e3f90..6f14e4a 100644 --- a/alpm/src/add.rs +++ b/alpm/src/add.rs @@ -1,8 +1,9 @@ -use crate::{Alpm, Error, LoadedPackage, Package}; +use std::ffi::CString; +use std::fmt; -use alpm_sys::*; +use crate::{Alpm, Error, LoadedPackage, Package, Result}; -use std::fmt; +use alpm_sys::*; #[doc(hidden)] pub unsafe trait IntoPkgAdd: fmt::Debug { @@ -27,7 +28,7 @@ unsafe impl IntoPkgAdd for LoadedPackage<'_> { } impl Alpm { - pub fn trans_add_pkg(&self, pkg: P) -> Result<(), AddError

> { + pub fn trans_add_pkg(&self, pkg: P) -> std::result::Result<(), AddError

> { let ret = unsafe { alpm_add_pkg(self.as_ptr(), pkg.as_alpm_pkg_t()) }; let ok = self.check_ret(ret); match ok { @@ -38,6 +39,38 @@ impl Alpm { Err(err) => Err(AddError { error: err, pkg }), } } + + /// Add a package to the transaction by name, searching all sync databases. + /// + /// This is a convenience method that searches all registered sync databases + /// for a package matching the given name and adds it to the transaction. + /// It avoids borrow checker conflicts that occur when holding `&Package` + /// references across transaction operations like `trans_prepare()`. + /// + /// # Example + /// ```no_run + /// use alpm::{Alpm, SigLevel, TransFlag}; + /// + /// let mut handle = Alpm::new("/", "/var/lib/pacman").unwrap(); + /// handle.register_syncdb("core", SigLevel::NONE).unwrap(); + /// let names = ["linux", "base"]; + /// + /// handle.trans_init(TransFlag::empty()).unwrap(); + /// for name in names { + /// handle.trans_add_pkg_by_name(name).unwrap(); + /// } + /// handle.trans_prepare().unwrap(); + /// handle.trans_commit().unwrap(); + /// handle.trans_release().unwrap(); + /// ``` + pub fn trans_add_pkg_by_name>>(&self, name: S) -> Result<()> { + let name = CString::new(name).unwrap(); + let dbs = unsafe { alpm_get_syncdbs(self.as_ptr()) }; + let pkg = unsafe { alpm_find_dbs_satisfier(self.as_ptr(), dbs, name.as_ptr()) }; + self.check_null(pkg)?; + let ret = unsafe { alpm_add_pkg(self.as_ptr(), pkg) }; + self.check_ret(ret) + } } #[derive(Debug)] diff --git a/alpm/src/remove.rs b/alpm/src/remove.rs index 7246265..f9c41e2 100644 --- a/alpm/src/remove.rs +++ b/alpm/src/remove.rs @@ -1,3 +1,5 @@ +use std::ffi::CString; + use crate::{Alpm, Package, Result}; use alpm_sys::*; @@ -7,4 +9,34 @@ impl Alpm { let ret = unsafe { alpm_remove_pkg(self.as_ptr(), pkg.as_ptr()) }; self.check_ret(ret) } + + /// Remove a package from the transaction by name. + /// + /// This is a convenience method that looks up the package in the local database + /// and adds it to the removal transaction. It avoids borrow checker conflicts + /// that occur when holding `&Package` references across transaction operations + /// like `trans_prepare()`. + /// + /// # Example + /// ```no_run + /// use alpm::{Alpm, TransFlag}; + /// + /// let mut handle = Alpm::new("/", "/var/lib/pacman").unwrap(); + /// let names = ["package1", "package2"]; + /// + /// handle.trans_init(TransFlag::empty()).unwrap(); + /// for name in names { + /// handle.trans_remove_pkg_by_name(name).unwrap(); + /// } + /// handle.trans_prepare().unwrap(); + /// handle.trans_commit().unwrap(); + /// handle.trans_release().unwrap(); + /// ``` + pub fn trans_remove_pkg_by_name>>(&self, name: S) -> Result<()> { + let name = CString::new(name).unwrap(); + let pkg = unsafe { alpm_db_get_pkg(alpm_get_localdb(self.as_ptr()), name.as_ptr()) }; + self.check_null(pkg)?; + let ret = unsafe { alpm_remove_pkg(self.as_ptr(), pkg) }; + self.check_ret(ret) + } } diff --git a/alpm/src/trans.rs b/alpm/src/trans.rs index 218839f..0d48a0f 100644 --- a/alpm/src/trans.rs +++ b/alpm/src/trans.rs @@ -319,6 +319,31 @@ mod tests { assert_eq!(deps.causing_pkg().unwrap(), "curl"); } + #[test] + fn test_trans_remove_by_name() { + let mut handle = Alpm::new("/", "tests/db").unwrap(); + let flags = TransFlag::DB_ONLY | TransFlag::NO_LOCK; + + handle.set_log_cb((), logcb); + handle.set_event_cb((), eventcb); + + handle.trans_init(flags).unwrap(); + handle.trans_remove_pkg_by_name("curl").unwrap(); + let err = handle.trans_prepare().unwrap_err(); + let err = err.data().unwrap(); + + let PrepareData::UnsatisfiedDeps(deps) = err else { + panic!("error is not UnsatisfiedDeps"); + }; + + assert_eq!(deps.len(), 1); + let deps = deps.first().unwrap(); + + assert_eq!(deps.depend().name(), "curl"); + assert_eq!(deps.target(), "pacman"); + assert_eq!(deps.causing_pkg().unwrap(), "curl"); + } + #[test] fn test_trans_conflict() { let mut handle = Alpm::new("/", "tests/db").unwrap();