Add new StorePath bindings

This commit is contained in:
John Ericson 2025-12-15 20:54:28 -05:00
parent 0eb73977ef
commit 65abededca
3 changed files with 71 additions and 1 deletions

8
Cargo.lock generated
View file

@ -132,6 +132,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hex-literal"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "home"
version = "0.5.11"
@ -301,12 +307,14 @@ version = "0.2.1"
dependencies = [
"anyhow",
"ctor",
"hex-literal",
"lazy_static",
"nix-bindings-store-sys",
"nix-bindings-util",
"nix-bindings-util-sys",
"pkg-config",
"tempfile",
"zerocopy",
]
[[package]]

View file

@ -17,9 +17,11 @@ nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" }
nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" }
nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" }
lazy_static = "1.4"
zerocopy = "0.8"
[dev-dependencies]
ctor = "0.2"
hex-literal = "0.4"
tempfile = "3.10"
[build-dependencies]

View file

@ -1,15 +1,21 @@
use std::ptr::NonNull;
use anyhow::Result;
use anyhow::{Context as _, Result};
use nix_bindings_store_sys as raw;
#[cfg(nix_at_least = "2.33")]
use nix_bindings_util::{check_call, context::Context};
use nix_bindings_util::{
result_string_init,
string_return::{callback_get_result_string, callback_get_result_string_data},
};
/// The size of a store path hash in bytes (20 bytes, decoded from nix32).
pub const STORE_PATH_HASH_SIZE: usize = 20;
pub struct StorePath {
raw: NonNull<raw::StorePath>,
}
impl StorePath {
/// Get the name of the store path.
///
@ -26,6 +32,43 @@ impl StorePath {
}
}
/// Get the hash part of the store path.
///
/// This returns the decoded hash (not the nix32-encoded string).
#[cfg(nix_at_least = "2.33")]
pub fn hash(&self) -> Result<[u8; STORE_PATH_HASH_SIZE]> {
let mut result = [0u8; STORE_PATH_HASH_SIZE];
let hash_part: &mut raw::store_path_hash_part = zerocopy::transmute_mut!(&mut result);
let mut ctx = Context::new();
unsafe {
check_call!(raw::store_path_hash(&mut ctx, self.as_ptr(), hash_part))?;
}
Ok(result)
}
/// Create a StorePath from hash and name components.
#[cfg(nix_at_least = "2.33")]
pub fn from_parts(hash: &[u8; STORE_PATH_HASH_SIZE], name: &str) -> Result<Self> {
let hash_part: &raw::store_path_hash_part = zerocopy::transmute_ref!(hash);
let mut ctx = Context::new();
let out_path = unsafe {
check_call!(raw::store_create_from_parts(
&mut ctx,
hash_part,
name.as_ptr() as *const i8,
name.len()
))?
};
NonNull::new(out_path)
.map(|ptr| unsafe { Self::new_raw(ptr) })
.context("store_create_from_parts returned null")
}
/// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings.
///
/// Construct a new `StorePath` by first cloning the C store path.
@ -81,6 +124,9 @@ impl Drop for StorePath {
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
#[test]
#[cfg(nix_at_least = "2.26" /* get_storedir */)]
fn store_path_name() {
@ -91,4 +137,18 @@ mod tests {
let store_path = store.parse_store_path(store_path_string.as_str()).unwrap();
assert_eq!(store_path.name().unwrap(), "bash-interactive-5.2p26");
}
#[test]
#[cfg(nix_at_least = "2.33")]
fn store_path_round_trip() {
let original_hash: [u8; STORE_PATH_HASH_SIZE] =
hex!("0123456789abcdef0011223344556677deadbeef");
let original_name = "foo.drv";
let store_path = StorePath::from_parts(&original_hash, original_name).unwrap();
// Round trip gets back what we started with
assert_eq!(store_path.hash().unwrap(), original_hash);
assert_eq!(store_path.name().unwrap(), original_name);
}
}