From f8852ca94569a15d2fd92cf6cc1e9e9948c1648a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 28 Sep 2021 15:37:26 -0400 Subject: [PATCH] repo: Add `auto_transaction` and `TransactionGuard` This gives auto-cancelling semantics on `Drop`, plus a nicer `.commit()` method on the transaction. Matches the currently private `_OstreeRepoAutoTransaction` in the C library. --- rust-bindings/rust/src/repo.rs | 41 +++++++++++++++++++++++++++- rust-bindings/rust/tests/util/mod.rs | 6 ++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/rust-bindings/rust/src/repo.rs b/rust-bindings/rust/src/repo.rs index 3b4961cf..432b3ee4 100644 --- a/rust-bindings/rust/src/repo.rs +++ b/rust-bindings/rust/src/repo.rs @@ -1,6 +1,6 @@ #[cfg(any(feature = "v2016_4", feature = "dox"))] use crate::RepoListRefsExtFlags; -use crate::{Checksum, ObjectName, ObjectType, Repo}; +use crate::{Checksum, ObjectName, ObjectType, Repo, RepoTransactionStats}; use ffi; use glib::ffi as glib_sys; use glib::{self, translate::*, Error, IsA}; @@ -34,12 +34,51 @@ unsafe fn from_glib_container_variant_set(ptr: *mut glib_sys::GHashTable) -> Has set } +/// An open transaction in the repository. +/// +/// This will automatically invoke [`ostree::Repo::abort_transaction`] when the value is dropped. +pub struct TransactionGuard<'a> { + /// Reference to the repository for this transaction. + repo: Option<&'a Repo>, +} + +impl<'a> TransactionGuard<'a> { + /// Commit this transaction. + pub fn commit>( + mut self, + cancellable: Option<&P>, + ) -> Result { + // Safety: This is the only function which mutates this option + let repo = self.repo.take().unwrap(); + repo.commit_transaction(cancellable) + } +} + +impl<'a> Drop for TransactionGuard<'a> { + fn drop(&mut self) { + if let Some(repo) = self.repo { + // TODO: better logging in ostree? + // See also https://github.com/ostreedev/ostree/issues/2413 + let _ = repo.abort_transaction(gio::NONE_CANCELLABLE); + } + } +} + impl Repo { /// Create a new `Repo` object for working with an OSTree repo at the given path. pub fn new_for_path>(path: P) -> Repo { Repo::new(&gio::File::for_path(path.as_ref())) } + /// A wrapper for [`prepare_transaction`] which ensures the transaction will be aborted when the guard goes out of scope. + pub fn auto_transaction>( + &self, + cancellable: Option<&P>, + ) -> Result { + let _ = self.prepare_transaction(cancellable)?; + Ok(TransactionGuard { repo: Some(self) }) + } + /// Return a copy of the directory file descriptor for this repository. #[cfg(any(feature = "v2016_4", feature = "dox"))] #[cfg_attr(feature = "dox", doc(cfg(feature = "v2016_4")))] diff --git a/rust-bindings/rust/tests/util/mod.rs b/rust-bindings/rust/tests/util/mod.rs index d4a83478..a51c0521 100644 --- a/rust-bindings/rust/tests/util/mod.rs +++ b/rust-bindings/rust/tests/util/mod.rs @@ -42,7 +42,8 @@ pub fn create_mtree(repo: &ostree::Repo) -> ostree::MutableTree { } pub fn commit(repo: &ostree::Repo, mtree: &ostree::MutableTree, ref_: &str) -> GString { - repo.prepare_transaction(NONE_CANCELLABLE) + let txn = repo + .auto_transaction(NONE_CANCELLABLE) .expect("prepare transaction"); let repo_file = repo .write_mtree(mtree, NONE_CANCELLABLE) @@ -60,8 +61,7 @@ pub fn commit(repo: &ostree::Repo, mtree: &ostree::MutableTree, ref_: &str) -> G ) .expect("write commit"); repo.transaction_set_ref(None, ref_, checksum.as_str().into()); - repo.commit_transaction(NONE_CANCELLABLE) - .expect("commit transaction"); + txn.commit(NONE_CANCELLABLE).expect("commit transaction"); checksum }