nix_bindings_store/path/
mod.rs1use std::ptr::NonNull;
2
3use anyhow::{Context as _, Result};
4use nix_bindings_store_sys as raw;
5#[cfg(nix_at_least = "2.33")]
6use nix_bindings_util::{check_call, context::Context};
7use nix_bindings_util::{
8 result_string_init,
9 string_return::{callback_get_result_string, callback_get_result_string_data},
10};
11
12pub const STORE_PATH_HASH_SIZE: usize = 20;
14
15#[cfg(nix_at_least = "2.33")]
16const _: () = assert!(std::mem::size_of::<raw::store_path_hash_part>() == STORE_PATH_HASH_SIZE);
17
18pub struct StorePath {
19 raw: NonNull<raw::StorePath>,
20}
21
22impl StorePath {
23 pub fn name(&self) -> Result<String> {
27 unsafe {
28 let mut r = result_string_init!();
29 raw::store_path_name(
30 self.as_ptr(),
31 Some(callback_get_result_string),
32 callback_get_result_string_data(&mut r),
33 );
34 r
35 }
36 }
37
38 #[cfg(nix_at_least = "2.33")]
42 pub fn hash(&self) -> Result<[u8; STORE_PATH_HASH_SIZE]> {
43 let mut result = [0u8; STORE_PATH_HASH_SIZE];
44 let hash_part: &mut raw::store_path_hash_part = zerocopy::transmute_mut!(&mut result);
45
46 let mut ctx = Context::new();
47
48 unsafe {
49 check_call!(raw::store_path_hash(&mut ctx, self.as_ptr(), hash_part))?;
50 }
51 Ok(result)
52 }
53
54 #[cfg(nix_at_least = "2.33")]
56 pub fn from_parts(hash: &[u8; STORE_PATH_HASH_SIZE], name: &str) -> Result<Self> {
57 let hash_part: &raw::store_path_hash_part = zerocopy::transmute_ref!(hash);
58
59 let mut ctx = Context::new();
60
61 let out_path = unsafe {
62 check_call!(raw::store_create_from_parts(
63 &mut ctx,
64 hash_part,
65 name.as_ptr() as *const i8,
66 name.len()
67 ))?
68 };
69
70 NonNull::new(out_path)
71 .map(|ptr| unsafe { Self::new_raw(ptr) })
72 .context("store_create_from_parts returned null")
73 }
74
75 pub unsafe fn new_raw_clone(raw: NonNull<raw::StorePath>) -> Self {
83 Self::new_raw(
84 NonNull::new(raw::store_path_clone(raw.as_ptr()))
85 .or_else(|| panic!("nix_store_path_clone returned a null pointer"))
86 .unwrap(),
87 )
88 }
89
90 pub unsafe fn new_raw(raw: NonNull<raw::StorePath>) -> Self {
99 StorePath { raw }
100 }
101
102 pub unsafe fn as_ptr(&self) -> *mut raw::StorePath {
110 self.raw.as_ptr()
111 }
112}
113
114impl Clone for StorePath {
115 fn clone(&self) -> Self {
116 unsafe { Self::new_raw_clone(self.raw) }
117 }
118}
119
120impl Drop for StorePath {
121 fn drop(&mut self) {
122 unsafe {
123 raw::store_path_free(self.as_ptr());
124 }
125 }
126}
127
128#[cfg(all(feature = "harmonia", nix_at_least = "2.33"))]
129mod harmonia;
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use hex_literal::hex;
135
136 #[test]
137 #[cfg(nix_at_least = "2.26" )]
138 fn store_path_name() {
139 let mut store = crate::store::Store::open(Some("dummy://"), []).unwrap();
140 let store_dir = store.get_storedir().unwrap();
141 let store_path_string =
142 format!("{store_dir}/rdd4pnr4x9rqc9wgbibhngv217w2xvxl-bash-interactive-5.2p26");
143 let store_path = store.parse_store_path(store_path_string.as_str()).unwrap();
144 assert_eq!(store_path.name().unwrap(), "bash-interactive-5.2p26");
145 }
146
147 #[test]
148 #[cfg(nix_at_least = "2.33")]
149 fn store_path_round_trip() {
150 let original_hash: [u8; STORE_PATH_HASH_SIZE] =
151 hex!("0123456789abcdef0011223344556677deadbeef");
152 let original_name = "foo.drv";
153
154 let store_path = StorePath::from_parts(&original_hash, original_name).unwrap();
155
156 assert_eq!(store_path.hash().unwrap(), original_hash);
158 assert_eq!(store_path.name().unwrap(), original_name);
159 }
160}