diff --git a/nixide-sys/build.rs b/nixide-sys/build.rs index 7bcfa95..95be485 100644 --- a/nixide-sys/build.rs +++ b/nixide-sys/build.rs @@ -1,5 +1,6 @@ use std::env; use std::fs; +use std::iter; use std::path::PathBuf; use bindgen::RustEdition; @@ -76,36 +77,41 @@ impl ParseCallbacks for BindfmtCallbacks { } } -const LIBS: &[&'static str] = &[ +const FEATURES: &[&'static str] = &[ #[cfg(feature = "nix-util-c")] - "nix-util-c", + "util", #[cfg(feature = "nix-store-c")] - "nix-store-c", + "store", #[cfg(feature = "nix-expr-c")] - "nix-expr-c", + "expr", #[cfg(feature = "nix-fetchers-c")] - "nix-fetchers-c", + "fetchers", #[cfg(feature = "nix-flake-c")] - "nix-flake-c", + "flake", #[cfg(feature = "nix-main-c")] - "nix-main-c", + "main", ]; fn main() { - let libs: Vec = LIBS + let libs: Vec = FEATURES .iter() - .map(|&name| { + .map(|&feature| { + let name = &format!("nix-{feature}-c"); 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() @@ -114,20 +120,7 @@ fn main() { .unique() .collect(); - // ::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); - + // build the libnixide-c extension cc::Build::new() // .cargo_output(true) // .cargo_warnings(true) @@ -135,15 +128,19 @@ fn main() { // .cargo_debug(cfg!(debug_assertions)) .cpp(true) .std("c++23") // libnix compiles against `-std=c++23` - .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())) + .cpp_link_stdlib("c++") // libstdc++ for GNU, c++ for Clang .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"); + .compile("nixide-c"); // libnixide-c + 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) @@ -156,8 +153,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 @@ -181,10 +178,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 = LIBS + builder = FEATURES .iter() - .map(|lib| { - let path = format!("include/{}.h", lib.strip_suffix("-c").unwrap()); + .map(|&feature| { + let path = format!("include/nix-{feature}.h"); 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 6660867..eb9776e 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,4 +13,8 @@ // #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 2e6d976..5648d7b 100644 --- a/nixide-sys/include/nix-fetchers.h +++ b/nixide-sys/include/nix-fetchers.h @@ -1,10 +1,12 @@ #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 e615ce0..e64bc61 100644 --- a/nixide-sys/include/nix-flake.h +++ b/nixide-sys/include/nix-flake.h @@ -1,10 +1,12 @@ #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 b2320d9..0001371 100644 --- a/nixide-sys/include/nix-main.h +++ b/nixide-sys/include/nix-main.h @@ -5,4 +5,8 @@ // #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 1c2ad65..855000a 100644 --- a/nixide-sys/include/nix-store.h +++ b/nixide-sys/include/nix-store.h @@ -4,9 +4,17 @@ // 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 9770d77..7608ffc 100644 --- a/nixide-sys/include/nix-util.h +++ b/nixide-sys/include/nix-util.h @@ -8,6 +8,9 @@ // the Nix C APIs for error handling. // #include -// #include + +// Nixide C API extensions for utilities. +// +#include #endif diff --git a/nixide-sys/libnixide-c/nixide_api_main.cc b/nixide-sys/libnixide-c/nixide_api_main.cc index 5e6fd92..2035bfc 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 nixide_register_plugin(nix_c_context * context, char * plugin) +nix_err nix_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 0962cf5..76046b9 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 nixide_register_plugin(nix_c_context * context, char * plugin); +nix_err nix_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 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/flake/flake_lock_flags.rs b/nixide/src/flake/flake_lock_flags.rs index 9f5427d..e9b1320 100644 --- a/nixide/src/flake/flake_lock_flags.rs +++ b/nixide/src/flake/flake_lock_flags.rs @@ -1,3 +1,4 @@ +use std::ffi::c_char; use std::ptr::NonNull; use super::{FlakeRef, FlakeSettings}; @@ -77,6 +78,60 @@ 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 { @@ -90,33 +145,176 @@ impl FlakeLockFlags { sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) }, }; - }); + })?; Ok(self) } - /// Adds an input override to the lock file that will be produced. - /// The [LockedFlake::lock] operation will not write to the lock file. + /// # Nix C API Internals /// - /// # Warning + /// This binding is **not provided by the Nix C API.** + /// It is instead **exposed by the Nixide C API extensions.** /// - /// Calling this function will implicitly set the [FlakeLockMode] to - /// [FlakeLockMode::Virtual] if `self.mode` is not [FlakeLockMode::Check]. + #[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 /// - /// # Arguments + /// This binding is **not provided by the Nix C API.** + /// It is instead **exposed by the Nixide C API extensions.** /// - /// * `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()?; + #[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; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_flake_lock_flags_add_input_override( + sys::nix_flake_lock_flags_set_reference_lock_file_path( ctx.as_ptr(), self.as_ptr(), - input_path, - flakeref.as_ptr(), - ); + path_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 deleted file mode 100644 index bfe44c5..0000000 --- a/nixide/src/flake/flake_reference.rs +++ /dev/null @@ -1,104 +0,0 @@ -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.rs b/nixide/src/flake/flakeref.rs index 7d6f719..bfe44c5 100644 --- a/nixide/src/flake/flakeref.rs +++ b/nixide/src/flake/flakeref.rs @@ -1,3 +1,104 @@ -pub struct FlakeRef {} +use std::ffi::{c_char, c_void}; +use std::ptr::{NonNull, null_mut}; -impl 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 + } +} diff --git a/nixide/src/flake/flake_reference_parse_flags.rs b/nixide/src/flake/flakeref_parse_flags.rs similarity index 100% rename from nixide/src/flake/flake_reference_parse_flags.rs rename to nixide/src/flake/flakeref_parse_flags.rs diff --git a/nixide/src/flake/mod.rs b/nixide/src/flake/mod.rs index 53f858c..68dfad2 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 216235f..f9f7e8c 100644 --- a/nixide/src/plugins.rs +++ b/nixide/src/plugins.rs @@ -1,5 +1,6 @@ use crate::NixideResult; use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr as _; @@ -8,3 +9,10 @@ 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 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