diff --git a/rust-bindings/rust/Cargo.toml b/rust-bindings/rust/Cargo.toml index 5f2a4445..61f929a5 100644 --- a/rust-bindings/rust/Cargo.toml +++ b/rust-bindings/rust/Cargo.toml @@ -29,6 +29,8 @@ members = [".", "sys"] [dependencies] bitflags = "1.2.1" +cap-std = { version = "0.24", optional = true} +io-lifetimes = { version = "0.5", optional = true} ffi = { package = "ostree-sys", path = "sys", version = "0.9.1" } gio = "0.14" glib = "0.14.4" @@ -42,8 +44,10 @@ thiserror = "1.0.20" maplit = "1.0.2" openat = "0.1.19" tempfile = "3" +cap-tempfile = "0.24" [features] +cap-std-apis = ["cap-std", "io-lifetimes", "v2017_10"] dox = ["ffi/dox"] v2014_9 = ["ffi/v2014_9"] v2015_7 = ["v2014_9", "ffi/v2015_7"] diff --git a/rust-bindings/rust/src/lib.rs b/rust-bindings/rust/src/lib.rs index ae2debb5..a1ba65e0 100644 --- a/rust-bindings/rust/src/lib.rs +++ b/rust-bindings/rust/src/lib.rs @@ -12,6 +12,8 @@ // Re-export our dependencies. See https://gtk-rs.org/blog/2021/06/22/new-release.html // "Dependencies are re-exported". Users will need e.g. `gio::File`, so this avoids // them needing to update matching versions. +#[cfg(feature = "cap-std-apis")] +pub use cap_std; pub use ffi; pub use gio; pub use glib; diff --git a/rust-bindings/rust/src/repo.rs b/rust-bindings/rust/src/repo.rs index 68895692..ec38ce83 100644 --- a/rust-bindings/rust/src/repo.rs +++ b/rust-bindings/rust/src/repo.rs @@ -1,5 +1,7 @@ #[cfg(any(feature = "v2016_4", feature = "dox"))] use crate::RepoListRefsExtFlags; +#[cfg(feature = "cap-std-apis")] +use crate::RepoMode; use crate::{Checksum, ObjectDetails, ObjectName, ObjectType, Repo, RepoTransactionStats}; use ffi::OstreeRepoListObjectsFlags; use glib::ffi as glib_sys; @@ -97,6 +99,26 @@ impl Repo { Repo::new(&gio::File::for_path(path.as_ref())) } + #[cfg(feature = "cap-std-apis")] + /// A version of [`open_at`] which uses cap-std. + pub fn open_at_dir(dir: &cap_std::fs::Dir, path: &str) -> Result { + use std::os::unix::io::AsRawFd; + crate::Repo::open_at(dir.as_raw_fd(), path, gio::NONE_CANCELLABLE) + } + + #[cfg(feature = "cap-std-apis")] + /// A version of [`create_at`] which uses cap-std, and also returns the opened repo. + pub fn create_at_dir( + dir: &cap_std::fs::Dir, + path: &str, + mode: RepoMode, + options: Option<&glib::Variant>, + ) -> Result { + use std::os::unix::io::AsRawFd; + crate::Repo::create_at(dir.as_raw_fd(), path, mode, options, gio::NONE_CANCELLABLE)?; + Repo::open_at_dir(dir, path) + } + /// A wrapper for [`prepare_transaction`] which ensures the transaction will be aborted when the guard goes out of scope. pub fn auto_transaction>( &self, diff --git a/rust-bindings/rust/tests/repo/mod.rs b/rust-bindings/rust/tests/repo/mod.rs index 007a0d83..2cfde3db 100644 --- a/rust-bindings/rust/tests/repo/mod.rs +++ b/rust-bindings/rust/tests/repo/mod.rs @@ -26,6 +26,26 @@ fn should_commit_content_to_repo_and_list_refs_again() { assert_eq!(checksum, refs["test"]); } +#[test] +#[cfg(feature = "cap-std-apis")] +fn cap_std_commit() { + let test_repo = CapTestRepo::new(); + + assert!(test_repo.repo.require_rev("nosuchrev").is_err()); + + let mtree = create_mtree(&test_repo.repo); + let checksum = commit(&test_repo.repo, &mtree, "test"); + + assert_eq!(test_repo.repo.require_rev("test").unwrap(), checksum); + + let repo2 = ostree::Repo::open_at_dir(&test_repo.dir, ".").unwrap(); + let refs = repo2 + .list_refs(None, NONE_CANCELLABLE) + .expect("failed to list refs"); + assert_eq!(1, refs.len()); + assert_eq!(checksum, refs["test"]); +} + #[test] fn repo_traverse_and_read() { let test_repo = TestRepo::new(); diff --git a/rust-bindings/rust/tests/util/mod.rs b/rust-bindings/rust/tests/util/mod.rs index a51c0521..2bc4efbf 100644 --- a/rust-bindings/rust/tests/util/mod.rs +++ b/rust-bindings/rust/tests/util/mod.rs @@ -28,6 +28,26 @@ impl TestRepo { } } +#[derive(Debug)] +#[cfg(feature = "cap-std-apis")] +pub struct CapTestRepo { + pub dir: cap_tempfile::TempDir, + pub repo: ostree::Repo, +} + +#[cfg(feature = "cap-std-apis")] +impl CapTestRepo { + pub fn new() -> Self { + Self::new_with_mode(ostree::RepoMode::Archive) + } + + pub fn new_with_mode(repo_mode: ostree::RepoMode) -> Self { + let dir = cap_tempfile::tempdir(cap_std::ambient_authority()).unwrap(); + let repo = ostree::Repo::create_at_dir(&dir, ".", repo_mode, None).expect("repo create"); + Self { dir, repo } + } +} + pub fn create_mtree(repo: &ostree::Repo) -> ostree::MutableTree { let mtree = ostree::MutableTree::new(); let file = gio::File::for_path(