feat: Add trans_add_pkg_by_name and trans_remove_pkg_by_name#63
feat: Add trans_add_pkg_by_name and trans_remove_pkg_by_name#63pfeifferj wants to merge 1 commit intoarchlinux:masterfrom
Conversation
These convenience methods perform database lookups internally, avoiding borrow checker conflicts when holding &Package references across transaction operations like trans_prepare(). trans_remove_pkg_by_name looks up package in localdb trans_add_pkg_by_name searches all registered sync databases Signed-off-by: Josephine Pfeiffer <hi@josie.lol>
|
I cant say exactly why you're running into this issue before seeing more of your code and seeing how you've structured the project generally. I don't think this PR is the way to work around this. Rather the code should be structured in a way that avoids this all together. I've written some example code that walks through why it works this way and how the situation can be avoided. If your code is substantially more complicated and it's difficult to avoid the borrow checker, I'm happy to take a look and see if there's a nice way to implement it. use alpm::*;
fn main() -> Result<()> {
// initialize alpm
let mut alpm = Alpm::new("/", "./db")?;
// set the event call back so we can see what alpm is doing
alpm.set_event_cb((), |e, _| {
if let Event::PackageOperationStart(e) = e.event()
&& let PackageOperation::Remove(e) = e.operation()
{
println!(" removing {}", e.name())
}
});
// select the first 10 packages that are not required by anything
let orphans: Vec<&Package> = alpm
.localdb()
.pkgs()
.iter()
.filter(|p| p.required_by().is_empty())
.take(10)
.collect();
// initialize a transaction
// this does not require &mut self as creating a new transaction just allocates an internal
// struct. and thus can't effect anything we may currently have a reference to.
alpm.trans_init(TransFlag::DB_ONLY | TransFlag::NO_HOOKS | TransFlag::NO_SCRIPTLET)?;
// add our orphans to the transaction
orphans.iter().try_for_each(|p| alpm.trans_remove_pkg(p))?;
// prepare the transaction.
//
// This function must take &mut self as it frees alpm->trans->add when it runs.
// it's possible users may have taken a reference to some of the values in this list
// so we need to make sure no references to any packages exist when we call this.
//
// however once we've given our packages to trans_{add, remove} we no longer hold any references
// to them so this just works (as long as we don't try to use `orphans` after this call)
alpm.trans_prepare()?;
// this would be a compile error.
// print!("{}", orphans.len());
println!("packages to remove...");
// we could go and re fetch our packages from the local db now that we've called
// trans_prepare().
// however we can also get them from the transaction itself
let orphans = alpm.trans_remove();
orphans.into_iter().for_each(|p| println!(" {}", p.name()));
println!("\ncomitting transaction...");
// this takes &mut self too
// we can't reference `orphans` again after this point, alpm deallocates them during the
// transaction
alpm.trans_commit()?;
Ok(())
}
The implementation in the PR lacks the ability to choose what repo you actually get the package from. This is something that any user should care about. I'm not convinced that these functions would be useful for borrow checker reasons. Though they may be useful just as general convenience functions. |
|
for posterity, here's the context from a side conversation we had: my thanku for the detailed breakdown + example code [1] https://github.com/pfeifferj/cockpit-pacman/blob/main/backend/src/alpm/transaction.rs |
These convenience methods perform database lookups internally, avoiding borrow checker conflicts when holding
&Packagereferences across transaction operations liketrans_prepare().trans_remove_pkg_by_namelooks up package in localdb;trans_add_pkg_by_namesearches all registered sync databases.For extra context, I was working on adding orphan cleanup features to cockpit-pacman and notice that when removing packages, the typical pattern hits a borrow checker conflict like e.g.:
The
&Packagereferences borrow from the handle, preventingtrans_prepare(&mut self). Users must work around this by collecting package names as owned strings first.These new methods encapsulate the lookup, so no
&Packageescapes:I thought it would make sense to upstream this, maybe. Looking forward to hearing your thoughts.