diff --git a/nixide-sys/build.rs b/nixide-sys/build.rs index 95be485..7bcfa95 100644 --- a/nixide-sys/build.rs +++ b/nixide-sys/build.rs @@ -1,6 +1,5 @@ use std::env; use std::fs; -use std::iter; use std::path::PathBuf; use bindgen::RustEdition; @@ -77,41 +76,36 @@ impl ParseCallbacks for BindfmtCallbacks { } } -const FEATURES: &[&'static str] = &[ +const LIBS: &[&'static str] = &[ #[cfg(feature = "nix-util-c")] - "util", + "nix-util-c", #[cfg(feature = "nix-store-c")] - "store", + "nix-store-c", #[cfg(feature = "nix-expr-c")] - "expr", + "nix-expr-c", #[cfg(feature = "nix-fetchers-c")] - "fetchers", + "nix-fetchers-c", #[cfg(feature = "nix-flake-c")] - "flake", + "nix-flake-c", #[cfg(feature = "nix-main-c")] - "main", + "nix-main-c", ]; fn main() { - let libs: Vec = FEATURES + let libs: Vec = LIBS .iter() - .map(|&feature| { - let name = &format!("nix-{feature}-c"); + .map(|&name| { pkg_config::probe_library(name).expect(&format!("Unable to find .pc file for {}", name)) }) .collect(); - #[allow(unused)] let include_paths: Vec = libs .clone() .into_iter() .map(|lib| lib.include_paths) .flatten() - .chain(iter::once(fs::canonicalize("./libnixide-c").unwrap())) .unique() .collect(); - - #[allow(unused)] let link_paths: Vec = libs .clone() .into_iter() @@ -120,7 +114,20 @@ fn main() { .unique() .collect(); - // build the libnixide-c extension + // ::DEBUG:DEBUG:: + // for path in link_paths { + // println!("cargo::rustc-link-lib={}", path.display()); + // } + // ::DEBUG:DEBUG:: + + let clang_args: Vec = vec!["-x", "c++", "-std=c++23"] + .into_iter() + .map(|s: &str| s.to_owned()) + .chain(include_paths.iter().map(|p| format!("-I{}", p.display()))) + .collect(); + + dbg!(&clang_args); + cc::Build::new() // .cargo_output(true) // .cargo_warnings(true) @@ -128,19 +135,15 @@ fn main() { // .cargo_debug(cfg!(debug_assertions)) .cpp(true) .std("c++23") // libnix compiles against `-std=c++23` - .cpp_link_stdlib("c++") // libstdc++ for GNU, c++ for Clang + .cpp_link_stdlib("stdc++") // use libstdc++ + .flags(["-fconcepts-diagnostics-depth=2"]) + .file("libnixide-c/nixide_api_flake.cc") + .file("libnixide-c/nixide_api_fetchers.cc") + // .files(LIBS.iter().map(|s| (*s).strip_prefix("nix-").unwrap().strip_suffix("-c").unwrap())) .opt_level((!cfg!(debug_assertions)) as u32 * 3) - .files(FEATURES.iter().map(|&feature| format!("libnixide-c/nixide_api_{feature}.cc"))) .includes(&include_paths) - .compile("nixide-c"); // libnixide-c + .compile("nixide"); - let clang_args: Vec = vec!["-x", "c++", "-std=c++23"] - .into_iter() - .map(|s: &str| s.to_owned()) - .chain(include_paths.iter().map(|p| format!("-I{}", p.display()))) - .collect(); - dbg!(&clang_args); - let mut builder = bindgen::Builder::default() .rust_edition(RustEdition::Edition2024) .clang_args(clang_args) @@ -153,8 +156,8 @@ fn main() { .rustfmt_configuration_file(std::fs::canonicalize("rustfmt.toml").ok()) // Control allow/block listing .allowlist_recursively(true) + // .allowlist_file(r".*nix_api_[a-z]+(_internal)?\.h") .allowlist_file(r".*nix_api_[a-z]+(/[a-z_]+)?\.h") - .allowlist_file(r".*nixide_api_[a-z]+(/[a-z_]+)?\.h") // .layout_tests(false) // DEBUG .use_core() // use ::core instead of ::std @@ -178,10 +181,10 @@ fn main() { .raw_line("/** These bindings were auto-generated for the Nixide project (https://github.com/cry128/nixide) */"); // Register the input headers we would like to generate bindings for - builder = FEATURES + builder = LIBS .iter() - .map(|&feature| { - let path = format!("include/nix-{feature}.h"); + .map(|lib| { + let path = format!("include/{}.h", lib.strip_suffix("-c").unwrap()); assert!(fs::exists(&path).unwrap()); // Invalidate the built crate if the binding headers change println!("cargo::rerun-if-changed={path}"); diff --git a/nixide-sys/include/nix-expr.h b/nixide-sys/include/nix-expr.h index eb9776e..6660867 100644 --- a/nixide-sys/include/nix-expr.h +++ b/nixide-sys/include/nix-expr.h @@ -2,8 +2,8 @@ #define NIXIDE_EXPR // Nix C API for the Nix expressions evaluator. -// #include +// #include // Nix C API for value manipulation. // @@ -13,8 +13,4 @@ // #include -// Nixide C API extensions for the Nix expressions evaluator. -// -#include - #endif diff --git a/nixide-sys/include/nix-fetchers.h b/nixide-sys/include/nix-fetchers.h index 5648d7b..2e6d976 100644 --- a/nixide-sys/include/nix-fetchers.h +++ b/nixide-sys/include/nix-fetchers.h @@ -1,12 +1,10 @@ #ifndef NIXIDE_FETCHERS #define NIXIDE_FETCHERS +// #include + // Nix C API for fetcher operations. // #include -// Nixide C API extensions for fetcher operations. -// -#include - #endif diff --git a/nixide-sys/include/nix-flake.h b/nixide-sys/include/nix-flake.h index e64bc61..e615ce0 100644 --- a/nixide-sys/include/nix-flake.h +++ b/nixide-sys/include/nix-flake.h @@ -1,12 +1,10 @@ #ifndef NIXIDE_FLAKE #define NIXIDE_FLAKE +// #include + // Nix C API for flake support. // #include -// Nixide C API extensions for flake support. -// -#include - #endif diff --git a/nixide-sys/include/nix-main.h b/nixide-sys/include/nix-main.h index 0001371..b2320d9 100644 --- a/nixide-sys/include/nix-main.h +++ b/nixide-sys/include/nix-main.h @@ -5,8 +5,4 @@ // #include -// Nixide C API extensions for CLI support. -// -#include - #endif diff --git a/nixide-sys/include/nix-store.h b/nixide-sys/include/nix-store.h index 855000a..1c2ad65 100644 --- a/nixide-sys/include/nix-store.h +++ b/nixide-sys/include/nix-store.h @@ -4,17 +4,9 @@ // Nix C API for store operations. // #include +// #include -// Nix C API for derivation operations that don't require a store. -// #include - -// Nix C API for store path operations that don't require a store. -// #include -// Nixide C API extensions for store operations. -// -#include - #endif diff --git a/nixide-sys/include/nix-util.h b/nixide-sys/include/nix-util.h index 7608ffc..9770d77 100644 --- a/nixide-sys/include/nix-util.h +++ b/nixide-sys/include/nix-util.h @@ -8,9 +8,6 @@ // the Nix C APIs for error handling. // #include - -// Nixide C API extensions for utilities. -// -#include +// #include #endif diff --git a/nixide-sys/libnixide-c/nixide_api_main.cc b/nixide-sys/libnixide-c/nixide_api_main.cc index 2035bfc..5e6fd92 100644 --- a/nixide-sys/libnixide-c/nixide_api_main.cc +++ b/nixide-sys/libnixide-c/nixide_api_main.cc @@ -7,7 +7,7 @@ extern "C" { -nix_err nix_register_plugin(nix_c_context * context, char * plugin) +nix_err nixide_register_plugin(nix_c_context * context, char * plugin) { if (context) context->last_err_code = NIX_OK; diff --git a/nixide-sys/libnixide-c/nixide_api_main.h b/nixide-sys/libnixide-c/nixide_api_main.h index 76046b9..0962cf5 100644 --- a/nixide-sys/libnixide-c/nixide_api_main.h +++ b/nixide-sys/libnixide-c/nixide_api_main.h @@ -8,7 +8,7 @@ extern "C" { #endif // NOTE: all plugins should be registered BEFORE `nix_init_plugins` is run -nix_err nix_register_plugin(nix_c_context * context, char * plugin); +nix_err nixide_register_plugin(nix_c_context * context, char * plugin); #ifdef __cplusplus } // extern "C" diff --git a/nixide/src/expr/realised_string.rs b/nixide/src/expr/realised_string.rs index 5b311c1..5e10470 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())), + Box::new(|_| StorePath::fake_path(state.store_ref().clone()).unwrap()), ), }) } @@ -98,7 +98,7 @@ impl<'a> RealisedString<'a> { err ) }); - StorePath::new(store, &path_str).unwrap_or_else(|err| { + StorePath::parse(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/flake/flake_lock_flags.rs b/nixide/src/flake/flake_lock_flags.rs index e9b1320..9f5427d 100644 --- a/nixide/src/flake/flake_lock_flags.rs +++ b/nixide/src/flake/flake_lock_flags.rs @@ -1,4 +1,3 @@ -use std::ffi::c_char; use std::ptr::NonNull; use super::{FlakeRef, FlakeSettings}; @@ -78,60 +77,6 @@ impl FlakeLockFlags { Ok(FlakeLockFlags { inner }) } - /// Adds an input override to the lock file that will be produced. - /// The [LockedFlake::lock] operation will not write to the lock file. - /// - /// # Warning - /// - /// Calling this function will implicitly set the [FlakeLockMode] to - /// [FlakeLockMode::Virtual] if `self.mode` is not [FlakeLockMode::Check]. - /// - /// # Arguments - /// - /// * `path` - The input name/path to override (must not be empty) - /// * `flakeref` - The flake reference to use as the override - /// - #[allow(unused)] - pub fn override_input(self, input: &str, flakeref: &FlakeRef) -> NixideResult { - // XXX: TODO: should `input` be wrapped as `format!("inputs.{input}")`? - let input_path = input.as_c_ptr()?; - - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_add_input_override( - ctx.as_ptr(), - self.as_ptr(), - input_path, - flakeref.as_ptr(), - ); - })?; - - Ok(self) - } - - /// Adds an input update to the lock file that will be produced. - /// Meaning the current pinned version is ignored, and updated - /// to the latest version the fetcher can resolve. - /// - /// This is equivalent to running `nix flake update ${input}`. - /// - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn update_input(self, input: &str) -> NixideResult { - // XXX: TODO: should `input` be wrapped as `format!("inputs.{input}")`? - let input_path = input.as_c_ptr()?; - - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_add_input_update(ctx.as_ptr(), self.as_ptr(), input_path); - })?; - - Ok(self) - } - - #[allow(unused)] pub fn set_mode(self, mode: FlakeLockMode) -> NixideResult { wrap::nix_fn!(|ctx: &ErrorContext| { match mode { @@ -145,176 +90,33 @@ impl FlakeLockFlags { sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) }, }; - })?; + }); Ok(self) } - /// # Nix C API Internals + /// Adds an input override to the lock file that will be produced. + /// The [LockedFlake::lock] operation will not write to the lock file. /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** + /// # Warning /// - #[allow(unused)] - pub fn recreate_lock_file(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_recreate_lock_file(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Nix C API Internals + /// Calling this function will implicitly set the [FlakeLockMode] to + /// [FlakeLockMode::Virtual] if `self.mode` is not [FlakeLockMode::Check]. /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** + /// # Arguments /// - #[allow(unused)] - pub fn update_lock_file(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_update_lock_file(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn write_lock_file(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_write_lock_file(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn commit_lock_file(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_commit_lock_file(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn allow_unlocked(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_allow_unlocked(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn fail_on_unlocked(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_fail_on_unlocked(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn use_registries(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_use_registries(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn apply_nix_config(self, value: bool) -> Self { - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_apply_nix_config(ctx.as_ptr(), self.as_ptr(), value) - }) - .unwrap(); - - self - } - - /// # Errors - /// Fails if the given `path` contains a NUL byte. - /// - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn input_lock_file_path(self, path: &str) -> NixideResult { - let path_ptr = path.as_c_ptr()? as *mut c_char; + /// * `path` - The input name/path to override (must not be empty) + /// * `flakeref` - The flake reference to use as the override + pub fn override_input(&mut self, path: &str, flakeref: &FlakeRef) -> NixideResult<()> { + let input_path = path.as_c_ptr()?; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_reference_lock_file_path( + sys::nix_flake_lock_flags_add_input_override( ctx.as_ptr(), self.as_ptr(), - path_ptr, - ) + input_path, + flakeref.as_ptr(), + ); }) - .unwrap(); - - Ok(self) - } - - /// # Errors - /// Fails if the given `path` contains a NUL byte. - /// - /// # Nix C API Internals - /// - /// This binding is **not provided by the Nix C API.** - /// It is instead **exposed by the Nixide C API extensions.** - /// - #[allow(unused)] - pub fn output_lock_file_path(self, path: &str) -> NixideResult { - let path_ptr = path.as_c_ptr()? as *mut c_char; - - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_set_output_lock_file_path( - ctx.as_ptr(), - self.as_ptr(), - path_ptr, - ) - }) - .unwrap(); - - Ok(self) } } diff --git a/nixide/src/flake/flake_reference.rs b/nixide/src/flake/flake_reference.rs new file mode 100644 index 0000000..bfe44c5 --- /dev/null +++ b/nixide/src/flake/flake_reference.rs @@ -0,0 +1,104 @@ +use std::ffi::{c_char, c_void}; +use std::ptr::{NonNull, null_mut}; + +use super::{FetchersSettings, FlakeRefParseFlags, FlakeSettings}; +use crate::NixideError; +use crate::errors::{ErrorContext, new_nixide_error}; +use crate::sys; +use crate::util::wrap; +use crate::util::wrappers::AsInnerPtr; + +pub struct FlakeRef { + inner: NonNull, + fragment: String, + + fetch_settings: FetchersSettings, + flake_settings: FlakeSettings, +} + +// impl Clone for FlakeReference { +// fn clone(&self) -> Self { +// wrap::nix_fn!(|ctx: &ErrorContext| unsafe { +// sys::nix_gc_incref(ctx.as_ptr(), self.as_ptr() as *mut c_void); +// }) +// .unwrap(); +// +// Self { +// inner: self.inner.clone(), +// fragment: self.fragment.clone(), +// } +// } +// } + +impl Drop for FlakeRef { + fn drop(&mut self) { + unsafe { + sys::nix_flake_reference_free(self.as_ptr()); + } + } +} + +impl AsInnerPtr for FlakeRef { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::NixFlakeReference { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::NixFlakeReference { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::NixFlakeReference { + unsafe { self.inner.as_mut() } + } +} + +impl FlakeRef { + /// Parse a flake reference from a string. + /// The string must be a valid flake reference, such as `github:owner/repo`. + /// It may also be suffixed with a `#` and a fragment, such as `github:owner/repo#something`, + /// in which case, the returned string will contain the fragment. + pub fn parse>(reference: S) -> Result { + let fetch_settings = FetchersSettings::new()?; + let flake_settings = FlakeSettings::new()?; + let parse_flags = FlakeRefParseFlags::new(&flake_settings)?; + + let mut ptr: *mut sys::NixFlakeReference = null_mut(); + let fragment = wrap::nix_string_callback!( + |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { + sys::nix_flake_reference_and_fragment_from_string( + ctx.as_ptr(), + fetch_settings.as_ptr(), + flake_settings.as_ptr(), + parse_flags.as_ptr(), + reference.as_ref().as_ptr() as *const c_char, + reference.as_ref().len(), + &mut ptr, + Some(callback), + userdata as *mut c_void, + ) + } + )?; + + match NonNull::new(ptr) { + Some(inner) => Ok(FlakeRef { + inner, + fragment, + fetch_settings, + flake_settings, + }), + None => Err(new_nixide_error!(NullPtr)), + } + } + + // XXX: TODO: is it possible to get the URI string itself? (minus the fragment part?) + /// Get a shared reference to the URI fragment part. + /// + #[inline] + #[allow(unused)] + pub fn fragment(&self) -> &str { + &self.fragment + } +} diff --git a/nixide/src/flake/flakeref_parse_flags.rs b/nixide/src/flake/flake_reference_parse_flags.rs similarity index 100% rename from nixide/src/flake/flakeref_parse_flags.rs rename to nixide/src/flake/flake_reference_parse_flags.rs diff --git a/nixide/src/flake/flakeref.rs b/nixide/src/flake/flakeref.rs index bfe44c5..7d6f719 100644 --- a/nixide/src/flake/flakeref.rs +++ b/nixide/src/flake/flakeref.rs @@ -1,104 +1,3 @@ -use std::ffi::{c_char, c_void}; -use std::ptr::{NonNull, null_mut}; +pub struct FlakeRef {} -use super::{FetchersSettings, FlakeRefParseFlags, FlakeSettings}; -use crate::NixideError; -use crate::errors::{ErrorContext, new_nixide_error}; -use crate::sys; -use crate::util::wrap; -use crate::util::wrappers::AsInnerPtr; - -pub struct FlakeRef { - inner: NonNull, - fragment: String, - - fetch_settings: FetchersSettings, - flake_settings: FlakeSettings, -} - -// impl Clone for FlakeReference { -// fn clone(&self) -> Self { -// wrap::nix_fn!(|ctx: &ErrorContext| unsafe { -// sys::nix_gc_incref(ctx.as_ptr(), self.as_ptr() as *mut c_void); -// }) -// .unwrap(); -// -// Self { -// inner: self.inner.clone(), -// fragment: self.fragment.clone(), -// } -// } -// } - -impl Drop for FlakeRef { - fn drop(&mut self) { - unsafe { - sys::nix_flake_reference_free(self.as_ptr()); - } - } -} - -impl AsInnerPtr for FlakeRef { - #[inline] - unsafe fn as_ptr(&self) -> *mut sys::NixFlakeReference { - self.inner.as_ptr() - } - - #[inline] - unsafe fn as_ref(&self) -> &sys::NixFlakeReference { - unsafe { self.inner.as_ref() } - } - - #[inline] - unsafe fn as_mut(&mut self) -> &mut sys::NixFlakeReference { - unsafe { self.inner.as_mut() } - } -} - -impl FlakeRef { - /// Parse a flake reference from a string. - /// The string must be a valid flake reference, such as `github:owner/repo`. - /// It may also be suffixed with a `#` and a fragment, such as `github:owner/repo#something`, - /// in which case, the returned string will contain the fragment. - pub fn parse>(reference: S) -> Result { - let fetch_settings = FetchersSettings::new()?; - let flake_settings = FlakeSettings::new()?; - let parse_flags = FlakeRefParseFlags::new(&flake_settings)?; - - let mut ptr: *mut sys::NixFlakeReference = null_mut(); - let fragment = wrap::nix_string_callback!( - |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { - sys::nix_flake_reference_and_fragment_from_string( - ctx.as_ptr(), - fetch_settings.as_ptr(), - flake_settings.as_ptr(), - parse_flags.as_ptr(), - reference.as_ref().as_ptr() as *const c_char, - reference.as_ref().len(), - &mut ptr, - Some(callback), - userdata as *mut c_void, - ) - } - )?; - - match NonNull::new(ptr) { - Some(inner) => Ok(FlakeRef { - inner, - fragment, - fetch_settings, - flake_settings, - }), - None => Err(new_nixide_error!(NullPtr)), - } - } - - // XXX: TODO: is it possible to get the URI string itself? (minus the fragment part?) - /// Get a shared reference to the URI fragment part. - /// - #[inline] - #[allow(unused)] - pub fn fragment(&self) -> &str { - &self.fragment - } -} +impl FlakeRef {} diff --git a/nixide/src/flake/mod.rs b/nixide/src/flake/mod.rs index 68dfad2..53f858c 100644 --- a/nixide/src/flake/mod.rs +++ b/nixide/src/flake/mod.rs @@ -1,13 +1,13 @@ mod fetchers_settings; mod flake_lock_flags; +mod flake_reference; +mod flake_reference_parse_flags; mod flake_settings; -mod flakeref; -mod flakeref_parse_flags; mod locked_flake; use fetchers_settings::FetchersSettings; use flake_lock_flags::{FlakeLockFlags, FlakeLockMode}; +use flake_reference::FlakeRef; +use flake_reference_parse_flags::FlakeRefParseFlags; pub use flake_settings::FlakeSettings; -use flakeref::FlakeRef; -use flakeref_parse_flags::FlakeRefParseFlags; pub use locked_flake::LockedFlake; diff --git a/nixide/src/plugins.rs b/nixide/src/plugins.rs index f9f7e8c..216235f 100644 --- a/nixide/src/plugins.rs +++ b/nixide/src/plugins.rs @@ -1,6 +1,5 @@ use crate::NixideResult; use crate::errors::ErrorContext; -use crate::stdext::AsCPtr as _; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr as _; @@ -9,10 +8,3 @@ pub fn load_plugins() -> NixideResult<()> { sys::nix_init_plugins(ctx.as_ptr()); }) } - -pub fn register_plugin>(path: S) -> NixideResult<()> { - let path_ptr = path.as_ref().into_c_ptr()?; - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_register_plugin(ctx.as_ptr(), path_ptr); - }) -} diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index bb0d398..a2dc000 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 _; +use crate::stdext::{AsCPtr as _, CCharPtrExt 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,6 +28,19 @@ 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 { @@ -58,17 +71,17 @@ impl Store { /// Returns an error if the store cannot be opened. /// pub fn open(uri: &str) -> NixideResult>> { - unsafe { Self::open_ptr(uri.as_c_ptr()?) } + Self::open_ptr(uri.as_c_ptr()?) } /// Opens a connection to the default Nix store. /// pub fn default() -> NixideResult>> { - unsafe { Self::open_ptr(null()) } + Self::open_ptr(null()) } #[inline] - unsafe fn open_ptr(uri_ptr: *const c_char) -> NixideResult>> { + 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()) @@ -77,6 +90,97 @@ 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 @@ -94,7 +198,7 @@ impl Store { ) } - /// Get the URI of a Nix store as a String. + /// Get the URI of a Nix store /// pub fn uri(&self) -> NixideResult { wrap::nix_string_callback!( @@ -109,8 +213,6 @@ 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 { @@ -123,6 +225,28 @@ 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 5ed68d3..417d2f4 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_char, c_void}; +use std::ffi::{CString, c_void}; use std::path::PathBuf; use std::ptr::NonNull; use std::rc::Rc; @@ -7,7 +7,6 @@ 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; @@ -19,7 +18,7 @@ use crate::util::wrappers::AsInnerPtr; /// pub struct StorePath { inner: NonNull, - store_ref: Rc>, + store: Rc>, } impl Clone for StorePath { @@ -31,7 +30,7 @@ impl Clone for StorePath { StorePath { inner, - store_ref: self.store_ref.clone(), + store: self.store.clone(), } } } @@ -72,33 +71,18 @@ impl StorePath { /// # Errors /// /// Returns an error if the path cannot be parsed. - /// - /// # 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 { + pub fn parse(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_ref: store, - }) + Ok(Self { inner, store }) } - pub fn fake_path(store: Rc>) -> Self { - Self::new(store, "/nix/store/00000000000000000000000000000000-fake").unwrap() + pub fn fake_path(store: Rc>) -> NixideResult { + Self::parse(store, "/nix/store/00000000000000000000000000000000-fake") } /// Get the name component of the store path. @@ -141,7 +125,7 @@ impl StorePath { |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { sys::nix_store_real_path( ctx.as_ptr(), - self.store_ref.borrow().as_ptr(), + self.store.borrow().as_ptr(), self.as_ptr(), Some(callback), userdata as *mut c_void, @@ -192,11 +176,11 @@ impl StorePath { store_path: *const sys::StorePath, | -> () { let callback: fn(&StorePath) = unsafe { (*userdata).inner.1 }; - let store_ref = unsafe { (*userdata).inner.0.clone() }; + let store = unsafe { (*userdata).inner.0.clone() }; let path = &StorePath { - inner: NonNull::new(unsafe { store_ref.borrow().as_ptr() } as *mut sys::StorePath).unwrap(), - store_ref, + inner: NonNull::new(unsafe { store.borrow().as_ptr() } as *mut sys::StorePath).unwrap(), + store, }; callback(&path); @@ -207,11 +191,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_ref.clone(), user_callback)); + (&raw mut (*state).inner).write((self.store.clone(), user_callback)); sys::nix_store_get_fs_closure( ctx.as_ptr(), - self.store_ref.borrow().as_ptr(), + self.store.borrow().as_ptr(), self.as_ptr(), flip, include_outputs, @@ -222,92 +206,7 @@ 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 52edf11..1a9c3ac 100644 --- a/nixide/src/store/tests.rs +++ b/nixide/src/store/tests.rs @@ -16,10 +16,11 @@ fn test_store_opening() { fn test_store_path_parse() { assert!(unsafe { matches!(LIBNIX_INIT_STATUS, Some(Ok(_))) }); - let store_ref = Store::default().expect("Failed to open store"); + let store = Store::default().expect("Failed to open store"); // Try parsing a well-formed store path - StorePath::fake_path(store_ref.clone()); + let result = StorePath::fake_path(&store.borrow()); + result.expect("idk hopefully this fails"); } #[test] @@ -27,10 +28,11 @@ fn test_store_path_parse() { fn test_store_path_clone() { assert!(unsafe { matches!(LIBNIX_INIT_STATUS, Some(Ok(_))) }); - let store_ref = Store::default().expect("Failed to open store"); + let store = Store::default().expect("Failed to open store"); // Try to get a valid store path by parsing - let path = StorePath::fake_path(store_ref.clone()); + let path = + StorePath::fake_path(&store.borrow()).expect("Failed to create `StorePath::fake_path`"); let cloned = path.clone(); // Assert that the cloned path has the same name as the original