diff --git a/nixide/src/expr/realised_string.rs b/nixide/src/expr/realised_string.rs index 5e10470..5b311c1 100644 --- a/nixide/src/expr/realised_string.rs +++ b/nixide/src/expr/realised_string.rs @@ -78,7 +78,7 @@ impl<'a> RealisedString<'a> { path: Self::parse_path(inner.as_ptr(), state.store_ref().clone()), children: LazyArray::new( size, - Box::new(|_| StorePath::fake_path(state.store_ref().clone()).unwrap()), + Box::new(|_| StorePath::fake_path(state.store_ref().clone())), ), }) } @@ -98,7 +98,7 @@ impl<'a> RealisedString<'a> { err ) }); - StorePath::parse(store, &path_str).unwrap_or_else(|err| { + StorePath::new(store, &path_str).unwrap_or_else(|err| { panic_issue_call_failed!( "`sys::nix_realised_string_get_buffer_(start|size)` invalid store path ({})", err diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index a2dc000..bb0d398 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -15,10 +15,10 @@ use std::rc::Rc; use crate::NixideResult; use crate::errors::ErrorContext; -use crate::stdext::{AsCPtr as _, CCharPtrExt as _}; +use crate::stdext::AsCPtr as _; use crate::sys; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; -use crate::util::{panic_issue_call_failed, wrap}; /// Nix store for managing packages and derivations. /// @@ -28,19 +28,6 @@ pub struct Store { inner: NonNull, } -// impl Clone for Store { -// fn clone(&self) -> Self { -// let inner = self.inner.clone(); -// -// wrap::nix_fn!(|ctx: &ErrorContext| unsafe { -// sys::nix_gc_incref(ctx.as_ptr(), self.as_ptr() as *mut c_void); -// }) -// .unwrap(); -// -// Self { inner } -// } -// } - impl AsInnerPtr for Store { #[inline] unsafe fn as_ptr(&self) -> *mut sys::Store { @@ -71,17 +58,17 @@ impl Store { /// Returns an error if the store cannot be opened. /// pub fn open(uri: &str) -> NixideResult>> { - Self::open_ptr(uri.as_c_ptr()?) + unsafe { Self::open_ptr(uri.as_c_ptr()?) } } /// Opens a connection to the default Nix store. /// pub fn default() -> NixideResult>> { - Self::open_ptr(null()) + unsafe { Self::open_ptr(null()) } } #[inline] - fn open_ptr(uri_ptr: *const c_char) -> NixideResult>> { + unsafe fn open_ptr(uri_ptr: *const c_char) -> NixideResult>> { let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { // XXX: TODO: allow args to be parsed instead of just `null_mut` sys::nix_store_open(ctx.as_ptr(), uri_ptr, null_mut()) @@ -90,97 +77,6 @@ impl Store { Ok(Rc::new(RefCell::new(Store { inner }))) } - /// Realize a store path. - /// - /// This builds/downloads the store path and all its dependencies, - /// making them available in the local store. - /// - /// # Arguments - /// - /// * `path` - The store path to realize - /// - /// # Returns - /// - /// A vector of (output_name, store_path) tuples for each realized output. - /// For example, a derivation might produce outputs like ("out", path1), ("dev", path2). - /// - /// # Errors - /// - /// Returns an error if the path cannot be realized. - /// - pub fn realise( - &self, - path: &StorePath, - user_callback: fn(&str, &StorePath), - ) -> NixideResult> { - wrap::nix_callback!( - |; userdata: fn(&str, &StorePath); - output_name_ptr: *const c_char, - output_path_ptr: *const sys::StorePath| - -> Vec<(String, StorePath)> { - // XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...) - // NOTE: this also ensures `output_name_ptr` isn't null - let output_name = output_name_ptr.to_utf8_string().unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); - - let inner = wrap::nix_ptr_fn!(|ctx| unsafe { - sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath) - }).unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); - let store_path = StorePath { inner }; - - let callback = unsafe { (*userdata).inner }; - callback(output_name.as_ref(), &store_path); - - (unsafe {(*userdata).retval }).append((output_name, store_path)); - }, - |callback, - state: *mut __UserData, - ctx: &ErrorContext| unsafe { - // register userdata - // WARNING: Using `write` instead of assignment via `=` - // WARNING: to not call `drop` on the old, uninitialized value. - (&raw mut (*state).inner).write(user_callback); - // register return value - (&raw mut (*state).retval).write(Vec::new()); - - sys::nix_store_realise( - ctx.as_ptr(), - self.as_ptr(), - path.as_ptr(), - (*state).inner_ptr() as *mut c_void, - Some(callback), - ); - } - ) - } - - /// Parse a store path string into a StorePath. - /// - /// This is a convenience method that wraps `StorePath::parse()`. - /// - /// # Arguments - /// - /// * `path` - The store path string (e.g., "/nix/store/...") - /// - /// # Errors - /// - /// Returns an error if the path cannot be parsed. - /// - /// # Example - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use nixide::Store; - /// # fn main() -> Result<(), Box> { - /// let store = Store::open(None)?; - /// let path = store.store_path("/nix/store/...")?; - /// # Ok(()) - /// # } - /// ``` - /// - pub fn store_path(&self, path: &str) -> NixideResult { - StorePath::parse(self, path) - } - /// Get the version of a Nix store /// /// If the store doesn't have a version (like the dummy store), returns None @@ -198,7 +94,7 @@ impl Store { ) } - /// Get the URI of a Nix store + /// Get the URI of a Nix store as a String. /// pub fn uri(&self) -> NixideResult { wrap::nix_string_callback!( @@ -213,6 +109,8 @@ impl Store { ) } + /// Get the store directory path of a Nix store. + /// pub fn store_dir(&self) -> NixideResult { wrap::nix_pathbuf_callback!( |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { @@ -225,28 +123,6 @@ impl Store { } ) } - - pub fn copy_closure_to(&self, dst_store: &Store, store_path: &StorePath) -> NixideResult<()> { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_store_copy_closure( - ctx.as_ptr(), - self.as_ptr(), - dst_store.as_ptr(), - store_path.as_ptr(), - ); - }) - } - - pub fn copy_closure_from(&self, src_store: &Store, store_path: &StorePath) -> NixideResult<()> { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_store_copy_closure( - ctx.as_ptr(), - src_store.as_ptr(), - self.as_ptr(), - store_path.as_ptr(), - ); - }) - } } impl Drop for Store { diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs index 417d2f4..5ed68d3 100644 --- a/nixide/src/store/path.rs +++ b/nixide/src/store/path.rs @@ -1,5 +1,5 @@ use std::cell::RefCell; -use std::ffi::{CString, c_void}; +use std::ffi::{CString, c_char, c_void}; use std::path::PathBuf; use std::ptr::NonNull; use std::rc::Rc; @@ -7,6 +7,7 @@ use std::rc::Rc; use super::Store; use crate::NixideResult; use crate::errors::{ErrorContext, new_nixide_error}; +use crate::stdext::CCharPtrExt as _; use crate::sys; use crate::util::panic_issue_call_failed; use crate::util::wrap; @@ -18,7 +19,7 @@ use crate::util::wrappers::AsInnerPtr; /// pub struct StorePath { inner: NonNull, - store: Rc>, + store_ref: Rc>, } impl Clone for StorePath { @@ -30,7 +31,7 @@ impl Clone for StorePath { StorePath { inner, - store: self.store.clone(), + store_ref: self.store_ref.clone(), } } } @@ -71,18 +72,33 @@ impl StorePath { /// # Errors /// /// Returns an error if the path cannot be parsed. - pub fn parse(store: Rc>, path: &str) -> NixideResult { + /// + /// # Example + /// + /// ```no_run + /// use nixide::{Store, StorePath}; + /// + /// fn main() { + /// let store_ref = Store::default().unwrap(); + /// let path = StorePath::new("/nix/store/f7gmvzd74wc1vlxzjdqy0af2381g8wr6-nix-manual-2.31.2-man").unwrap(); + /// } + /// ``` + /// + pub fn new(store: Rc>, path: &str) -> NixideResult { let c_path = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { sys::nix_store_parse_path(ctx.as_ptr(), store.borrow().as_ptr(), c_path.as_ptr()) })?; - Ok(Self { inner, store }) + Ok(Self { + inner, + store_ref: store, + }) } - pub fn fake_path(store: Rc>) -> NixideResult { - Self::parse(store, "/nix/store/00000000000000000000000000000000-fake") + pub fn fake_path(store: Rc>) -> Self { + Self::new(store, "/nix/store/00000000000000000000000000000000-fake").unwrap() } /// Get the name component of the store path. @@ -125,7 +141,7 @@ impl StorePath { |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { sys::nix_store_real_path( ctx.as_ptr(), - self.store.borrow().as_ptr(), + self.store_ref.borrow().as_ptr(), self.as_ptr(), Some(callback), userdata as *mut c_void, @@ -176,11 +192,11 @@ impl StorePath { store_path: *const sys::StorePath, | -> () { let callback: fn(&StorePath) = unsafe { (*userdata).inner.1 }; - let store = unsafe { (*userdata).inner.0.clone() }; + let store_ref = unsafe { (*userdata).inner.0.clone() }; let path = &StorePath { - inner: NonNull::new(unsafe { store.borrow().as_ptr() } as *mut sys::StorePath).unwrap(), - store, + inner: NonNull::new(unsafe { store_ref.borrow().as_ptr() } as *mut sys::StorePath).unwrap(), + store_ref, }; callback(&path); @@ -191,11 +207,11 @@ impl StorePath { // register userdata // WARNING: Using `write` instead of assignment via `=` // WARNING: to not call `drop` on the old, uninitialized value. - (&raw mut (*state).inner).write((self.store.clone(), user_callback)); + (&raw mut (*state).inner).write((self.store_ref.clone(), user_callback)); sys::nix_store_get_fs_closure( ctx.as_ptr(), - self.store.borrow().as_ptr(), + self.store_ref.borrow().as_ptr(), self.as_ptr(), flip, include_outputs, @@ -206,7 +222,92 @@ impl StorePath { ) } + pub fn copy_closure_to(&self, dst_store_ref: Rc>) -> NixideResult<()> { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_store_copy_closure( + ctx.as_ptr(), + self.store_ref.borrow().as_ptr(), + dst_store_ref.borrow().as_ptr(), + self.as_ptr(), + ); + }) + } + + pub fn copy_closure_from(&self, src_store_ref: Rc>) -> NixideResult<()> { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_store_copy_closure( + ctx.as_ptr(), + src_store_ref.borrow().as_ptr(), + self.store_ref.borrow().as_ptr(), + self.as_ptr(), + ); + }) + } + + /// Realize a store path. + /// + /// This builds/downloads the store path and all its dependencies, + /// making them available in the local store. + /// + /// # Arguments + /// + /// * `path` - The store path to realize + /// + /// # Returns + /// + /// A vector of (output_name, store_path) tuples for each realized output. + /// For example, a derivation might produce outputs like ("out", path1), ("dev", path2). + /// + /// # Errors + /// + /// Returns an error if the path cannot be realized. + /// + pub fn realise( + &self, + user_callback: fn(&str, &StorePath), + ) -> NixideResult> { + wrap::nix_callback!( + |; userdata: (Rc>, fn(&str, &StorePath)); + output_name_ptr: *const c_char, + output_path_ptr: *const sys::StorePath| + -> Vec<(String, StorePath)> { + // XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...) + // NOTE: this also ensures `output_name_ptr` isn't null + let output_name = output_name_ptr.to_utf8_string().unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let inner = wrap::nix_ptr_fn!(|ctx| unsafe { + sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath) + }).unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let store_ref = unsafe { (*userdata).inner.0.clone() }; + let callback = unsafe { (*userdata).inner.1 }; + + let store_path = StorePath { inner, store_ref }; + + callback(output_name.as_ref(), &store_path); + + unsafe { (*userdata).retval.push((output_name, store_path)) }; + }, + |callback, + state: *mut __UserData, + ctx: &ErrorContext| unsafe { + // register userdata + // WARNING: Using `write` instead of assignment via `=` + // WARNING: to not call `drop` on the old, uninitialized value. + (&raw mut (*state).inner).write((self.store_ref.clone(), user_callback)); + // register return value + (&raw mut (*state).retval).write(Vec::new()); + + sys::nix_store_realise( + ctx.as_ptr(), + self.store_ref.borrow().as_ptr(), + self.as_ptr(), + (*state).inner_ptr() as *mut c_void, + Some(callback), + ); + } + ) + } + // XXX: TODO: nix 2.34.4 adds a LOT here (ie especially around derivations) - // XXX: TODO: it also removes nix_store_path* functions (ie nix_store_path_free)? - // XXX: TODO: why?? try and research this, maybe they didn't mean to?? } diff --git a/nixide/src/store/tests.rs b/nixide/src/store/tests.rs index 1a9c3ac..52edf11 100644 --- a/nixide/src/store/tests.rs +++ b/nixide/src/store/tests.rs @@ -16,11 +16,10 @@ fn test_store_opening() { fn test_store_path_parse() { assert!(unsafe { matches!(LIBNIX_INIT_STATUS, Some(Ok(_))) }); - let store = Store::default().expect("Failed to open store"); + let store_ref = Store::default().expect("Failed to open store"); // Try parsing a well-formed store path - let result = StorePath::fake_path(&store.borrow()); - result.expect("idk hopefully this fails"); + StorePath::fake_path(store_ref.clone()); } #[test] @@ -28,11 +27,10 @@ fn test_store_path_parse() { fn test_store_path_clone() { assert!(unsafe { matches!(LIBNIX_INIT_STATUS, Some(Ok(_))) }); - let store = Store::default().expect("Failed to open store"); + let store_ref = Store::default().expect("Failed to open store"); // Try to get a valid store path by parsing - let path = - StorePath::fake_path(&store.borrow()).expect("Failed to create `StorePath::fake_path`"); + let path = StorePath::fake_path(store_ref.clone()); let cloned = path.clone(); // Assert that the cloned path has the same name as the original