diff --git a/flake.lock b/flake.lock index 09cd3d7..e6cfded 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1776025235, - "narHash": "sha256-zZIrW6G6GiFkpfFrOVjxYAVcXoKBMHv06kaUTOiqOq0=", + "lastModified": 1774682177, + "narHash": "sha256-OVbuJnJLlbHE28eRMudjtA6NXz/ifuXSho79gvh6GHY=", "owner": "nix-community", "repo": "fenix", - "rev": "dfe3f197bf8f3e95d705b7323b46e3a087c88972", + "rev": "e0f515387df77b9fdbaaf81e7f866f0365474c18", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1775710090, - "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", + "lastModified": 1775036866, + "narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4c1018dae018162ec878d42fec712642d214fdfa", + "rev": "6201e203d09599479a3b3450ed24fa81537ebc4e", "type": "github" }, "original": { @@ -47,11 +47,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1775939146, - "narHash": "sha256-YI8Hkuc2PSEJNwC8Qj3/DlMt1JfRv0MqzrnH/Mh9i5s=", + "lastModified": 1774569884, + "narHash": "sha256-E8iWEPzg7OnE0XXXjo75CX7xFauqzJuGZ5wSO9KS8Ek=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "09dc7aca674f53032f9d819f6fea01046e3d473f", + "rev": "443ddcddd0c73b07b799d052f5ef3b448c2f3508", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 19c113a..a64229f 100644 --- a/flake.nix +++ b/flake.nix @@ -24,6 +24,7 @@ allowUnfree = false; allowBroken = false; overlays = builtins.attrValues self.overlays or {}; + # config.replaceStdenv = {pkgs}: with pkgs; llvmPackages_21.stdenv; }; forAllSystems = f: @@ -35,6 +36,9 @@ }); in { overlays = { + default = self: super: { + libclang = super.llvmPackages_21.libclang; + }; fenix = inputs.fenix.overlays.default; }; @@ -43,126 +47,129 @@ pkgs, lib, ... - }: let - nixForBindings = pkgs.nixVersions.nix_2_34; - inherit (pkgs.rustc) llvmPackages; - in { - default = pkgs.mkShell rec { - name = "nixide"; - shell = "${pkgs.bash}/bin/bash"; - strictDeps = true; + }: { + default = let + nixForBindings = pkgs.nixVersions.nix_2_34; + inherit (pkgs.rustc) llvmPackages; + in + pkgs.mkShell rec { + name = "nixide"; + shell = "${pkgs.bash}/bin/bash"; + strictDeps = true; - # packages we need at runtime - packages = with pkgs; [ - rustc - llvmPackages.lld - llvmPackages.lldb - # lldb + # packages we need at runtime + packages = with pkgs; [ + rustc + llvmPackages.lld + llvmPackages.lldb + # lldb - cargo - cargo-c - cargo-llvm-cov - cargo-nextest + cargo + cargo-c + cargo-llvm-cov + cargo-nextest - clang - clang-tools + clang # DEBUG + clang-tools # DEBUG - libcxx + libcxx - rust-analyzer-unwrapped - (rustfmt.override {asNightly = true;}) - clippy - taplo - ]; + rust-analyzer-unwrapped + (rustfmt.override {asNightly = true;}) + clippy + taplo + ]; - # packages we need at build time - nativeBuildInputs = with pkgs; [ - pkg-config - glibc.dev - nixForBindings.dev + # packages we need at build time + nativeBuildInputs = with pkgs; [ + pkg-config + glibc.dev + nixForBindings.dev - rustPlatform.bindgenHook - ]; + rustPlatform.bindgenHook + ]; - # packages we link against - buildInputs = [ - # pkgs.stdenv.cc + # packages we link against + buildInputs = with pkgs; [ + stdenv.cc - nixForBindings - ]; + nixForBindings + ]; - env = { - LD_LIBRARY_PATH = builtins.toString (lib.makeLibraryPath buildInputs); - LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib"; + env = let + inherit (llvmPackages) llvm libclang; + in { + LD_LIBRARY_PATH = builtins.toString (lib.makeLibraryPath buildInputs); + LIBCLANG_PATH = "${libclang.lib}/lib"; - RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; - BINDGEN_EXTRA_CLANG_ARGS = "--sysroot=${pkgs.glibc.dev}"; + RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; + BINDGEN_EXTRA_CLANG_ARGS = "--sysroot=${pkgs.glibc.dev}"; - # `cargo-llvm-cov` reads these environment variables to find these binaries, - # which are needed to run the tests - LLVM_COV = "${llvmPackages.llvm}/bin/llvm-cov"; - LLVM_PROFDATA = "${llvmPackages.llvm}/bin/llvm-profdata"; + # `cargo-llvm-cov` reads these environment variables to find these binaries, + # which are needed to run the tests + LLVM_COV = "${llvm}/bin/llvm-cov"; + LLVM_PROFDATA = "${llvm}/bin/llvm-profdata"; + }; }; - }; - # nightly = let - # nixForBindings = pkgs.nixVersions.nix_2_34; - # inherit (pkgs.rustc) llvmPackages; - # in - # pkgs.mkShell rec { - # name = "nixide"; - # shell = "${pkgs.bash}/bin/bash"; - # strictDeps = true; + nightly = let + nixForBindings = pkgs.nixVersions.nix_2_34; + inherit (pkgs.rustc) llvmPackages; + in + pkgs.mkShell rec { + name = "nixide"; + shell = "${pkgs.bash}/bin/bash"; + strictDeps = true; - # # packages we need at runtime - # packages = with pkgs; [ - # llvmPackages.lld - # lldb - # (pkgs.fenix.complete.withComponents [ - # "cargo" - # "clippy" - # "rust-src" - # "rustc" - # "rustfmt" - # ]) - # rust-analyzer-nightly + # packages we need at runtime + packages = with pkgs; [ + llvmPackages.lld + lldb + (pkgs.fenix.complete.withComponents [ + "cargo" + "clippy" + "rust-src" + "rustc" + "rustfmt" + ]) + rust-analyzer-nightly - # # cargo-c - # # cargo-llvm-cov - # # cargo-nextest - # ]; + # cargo-c + # cargo-llvm-cov + # cargo-nextest + ]; - # # packages we need at build time - # nativeBuildInputs = with pkgs; [ - # pkg-config - # glibc.dev - # nixForBindings.dev + # packages we need at build time + nativeBuildInputs = with pkgs; [ + pkg-config + glibc.dev + nixForBindings.dev - # rustPlatform.bindgenHook - # ]; + rustPlatform.bindgenHook + ]; - # # packages we link against - # buildInputs = with pkgs; [ - # stdenv.cc + # packages we link against + buildInputs = with pkgs; [ + stdenv.cc - # nixForBindings - # ]; + nixForBindings + ]; - # env = let - # inherit (llvmPackages) llvm libclang; - # in { - # LD_LIBRARY_PATH = builtins.toString (lib.makeLibraryPath buildInputs); - # LIBCLANG_PATH = "${libclang.lib}/lib"; + env = let + inherit (llvmPackages) llvm libclang; + in { + LD_LIBRARY_PATH = builtins.toString (lib.makeLibraryPath buildInputs); + LIBCLANG_PATH = "${libclang.lib}/lib"; - # RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; - # BINDGEN_EXTRA_CLANG_ARGS = "--sysroot=${pkgs.glibc.dev}"; + RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; + BINDGEN_EXTRA_CLANG_ARGS = "--sysroot=${pkgs.glibc.dev}"; - # # `cargo-llvm-cov` reads these environment variables to find these binaries, - # # which are needed to run the tests - # LLVM_COV = "${llvm}/bin/llvm-cov"; - # LLVM_PROFDATA = "${llvm}/bin/llvm-profdata"; - # }; - # }; + # `cargo-llvm-cov` reads these environment variables to find these binaries, + # which are needed to run the tests + LLVM_COV = "${llvm}/bin/llvm-cov"; + LLVM_PROFDATA = "${llvm}/bin/llvm-profdata"; + }; + }; } ); }; diff --git a/nixide-sys/build.rs b/nixide-sys/build.rs index d9e74fc..95be485 100644 --- a/nixide-sys/build.rs +++ b/nixide-sys/build.rs @@ -128,6 +128,7 @@ 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 .opt_level((!cfg!(debug_assertions)) as u32 * 3) .files(FEATURES.iter().map(|&feature| format!("libnixide-c/nixide_api_{feature}.cc"))) .includes(&include_paths) diff --git a/nixide-sys/libnixide-c/nixide_api_main.cc b/nixide-sys/libnixide-c/nixide_api_main.cc index 18a5b43..2035bfc 100644 --- a/nixide-sys/libnixide-c/nixide_api_main.cc +++ b/nixide-sys/libnixide-c/nixide_api_main.cc @@ -13,7 +13,7 @@ nix_err nix_register_plugin(nix_c_context * context, char * plugin) context->last_err_code = NIX_OK; if (plugin == nullptr) { // TODO: should this be `NIX_ERR_RECOVERABLE` or `NIX_ERR_UNKNOWN`? - return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Plugin is null"); + return nix_set_err_msg(context, NIX_ERR_RECOVERABLE, "Plugin is null"); } void * handle = dlopen("libnixmainc.so", RTLD_LAZY | RTLD_GLOBAL); diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index 13a3c6d..060ddd9 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -21,15 +21,14 @@ // static std::optional programName; // }; -use std::ffi::CString; use std::ffi::c_uint; use std::ffi::c_void; use std::ptr::NonNull; use super::{NixError, NixideResult}; -use crate::errors::ToNixideResult as _; -use crate::stdext::CCharPtrExt as _; +use crate::stdext::{AsCPtr as _, CCharPtrExt as _}; use crate::sys; +use crate::util::panic_issue_call_failed; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; @@ -101,7 +100,7 @@ impl Into> for &ErrorContext { }; let msg = match self.get_msg() { Some(msg) => msg, - None => String::new(), + None => return Ok(()), }; let err = match inner { @@ -110,8 +109,13 @@ impl Into> for &ErrorContext { sys::NixErr::Overflow => NixError::Overflow, sys::NixErr::Key => NixError::KeyNotFound(None), sys::NixErr::NixError => NixError::ExprEval { - name: self.get_nix_err_name().unwrap(), - info_msg: self.get_nix_err_info_msg().unwrap_or_else(|| String::new()), + name: self + .get_nix_err_name() + .unwrap_or_else(|| panic_issue_call_failed!()), + + info_msg: self + .get_nix_err_info_msg() + .unwrap_or_else(|| panic_issue_call_failed!()), }, // XXX: WARNING: Recoverable only exists in later version of Nix @@ -190,11 +194,11 @@ impl ErrorContext { /// #[allow(unused)] pub fn set_err(&self, err: NixError, msg: &str) -> NixideResult<()> { - let ctx = unsafe { self.as_ptr() }; - let c_msg = CString::new(msg).to_nixide_result()?; + let ptr = unsafe { self.as_ptr() }; + assert!(!ptr.is_null(), ""); unsafe { - sys::nix_set_err_msg(ctx, err.err_code(), c_msg.as_ptr()); + sys::nix_set_err_msg(ptr, err.err_code(), msg.as_c_ptr()?); } Ok(()) @@ -213,7 +217,7 @@ impl ErrorContext { /// /// This function **never fails**. /// - pub fn get_err(&self) -> Option { + fn get_err(&self) -> Option { match unsafe { sys::nix_err_code(self.as_ptr()) } { sys::NixErr::Ok => None, err => Some(err), @@ -250,7 +254,7 @@ impl ErrorContext { /// Hence we can just test whether the returned pointer is a `NULL` pointer, /// and avoid passing in a [NixCContext] struct. /// - pub fn get_msg(&self) -> Option { + fn get_msg(&self) -> Option { // NOTE: an Err here only occurs when `self.get_code() == Ok(())` let mut n: c_uint = 0; let result = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { diff --git a/nixide/src/errors/error.rs b/nixide/src/errors/error.rs index 0894cdd..95c8379 100644 --- a/nixide/src/errors/error.rs +++ b/nixide/src/errors/error.rs @@ -5,16 +5,6 @@ use crate::sys; pub type NixideResult = Result; -pub(crate) trait ToNixideResult { - fn to_nixide_result(self) -> NixideResult; -} - -impl ToNixideResult for Result { - fn to_nixide_result(self) -> NixideResult { - Err(new_nixide_error!(StringNulByte)) - } -} - #[derive(Debug, Clone)] pub enum NixideError { /// # Warning @@ -144,3 +134,16 @@ impl Display for NixideError { } } } + +// pub trait AsErr { +// fn as_err(self) -> Result<(), T>; +// } + +// impl AsErr for Option { +// fn as_err(self) -> Result<(), NixideError> { +// match self { +// Some(err) => Err(err), +// None => Ok(()), +// } +// } +// } diff --git a/nixide/src/errors/mod.rs b/nixide/src/errors/mod.rs index 9be349f..c194728 100644 --- a/nixide/src/errors/mod.rs +++ b/nixide/src/errors/mod.rs @@ -4,6 +4,6 @@ mod context; mod nix_error; pub(crate) use context::ErrorContext; +pub(crate) use error::new_nixide_error; pub use error::{NixideError, NixideResult}; -pub(crate) use error::{ToNixideResult, new_nixide_error}; pub use nix_error::NixError; diff --git a/nixide/src/expr/eval_state.rs b/nixide/src/expr/eval_state.rs index 2f7c20a..7cee8e4 100644 --- a/nixide/src/expr/eval_state.rs +++ b/nixide/src/expr/eval_state.rs @@ -1,10 +1,11 @@ use std::cell::RefCell; -use std::ffi::CString; use std::ptr::NonNull; use std::rc::Rc; +use crate::stdext::AsCPtr as _; + use super::Value; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; use crate::sys; use crate::util::wrappers::AsInnerPtr; use crate::util::{panic_issue_call_failed, wrap}; @@ -21,6 +22,22 @@ pub struct EvalState { store: Rc>, } +// impl Clone for EvalState { +// 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, +// store: self.store.clone(), +// } +// } +// } + impl AsInnerPtr for EvalState { #[inline] unsafe fn as_ptr(&self) -> *mut sys::EvalState { @@ -70,8 +87,8 @@ impl EvalState { /// Returns an error if evaluation fails. /// pub fn interpret(&self, expr: &str, path: &str) -> NixideResult { - let c_expr = CString::new(expr).to_nixide_result()?; - let c_path = CString::new(path).to_nixide_result()?; + let expr = expr.as_c_ptr()?; + let path = path.as_c_ptr()?; // Allocate value for result // XXX: TODO: create a method for this (``) @@ -82,15 +99,10 @@ impl EvalState { // Evaluate expression wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_expr_eval_from_string( - ctx.as_ptr(), - self.as_ptr(), - c_expr.as_ptr(), - c_path.as_ptr(), - value.as_ptr(), - ); - Value::from((value, self.inner_ref().clone())) + sys::nix_expr_eval_from_string(ctx.as_ptr(), self.as_ptr(), expr, path, value.as_ptr()); + value }) + .map(|ptr| Value::from((ptr, self.inner_ref().clone()))) } } diff --git a/nixide/src/expr/values/attrs.rs b/nixide/src/expr/values/attrs.rs index c937214..c6c87b8 100644 --- a/nixide/src/expr/values/attrs.rs +++ b/nixide/src/expr/values/attrs.rs @@ -1,16 +1,15 @@ use std::cell::RefCell; -use std::ffi::CString; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ptr::{self, NonNull}; use std::rc::Rc; use super::{NixThunk, NixValue, Value}; -use crate::errors::{ErrorContext, NixideError, ToNixideResult as _}; -use crate::stdext::CCharPtrExt; +use crate::NixError; +use crate::errors::{ErrorContext, NixideError}; +use crate::stdext::{AsCPtr, CCharPtrExt}; use crate::sys; use crate::util::wrappers::AsInnerPtr; use crate::util::{panic_issue_call_failed, wrap}; -use crate::{NixError, NixideResult}; pub struct NixAttrs { inner: NonNull, @@ -171,58 +170,55 @@ impl NixAttrs { Some(name) } - pub fn get(&self, name: T) -> NixideResult> + pub fn get(&self, name: T) -> Option where T: AsRef, { - let c_name = CString::new(name.as_ref()).to_nixide_result()?; - let result = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { sys::nix_get_attr_byname( ctx.as_ptr(), self.as_ptr(), self.state.borrow().as_ptr(), - c_name.as_ptr(), + name.as_ref() + .into_c_ptr() + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)), ) }); match result { - Ok(inner) => Ok(Some(Value::from((inner, self.state.clone())))), + Ok(inner) => Some(Value::from((inner, self.state.clone()))), Err(NixideError::NixError { err: NixError::KeyNotFound(_), .. - }) => Ok(None), - Err(err) => Err(err), + }) => None, + Err(err) => panic_issue_call_failed!("{}", err), } } - pub fn get_lazy(&self, name: T) -> NixideResult> + pub fn get_lazy(&self, name: T) -> Option where T: AsRef, { - let c_name = CString::new(name.as_ref()).to_nixide_result()?; - let result = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { sys::nix_get_attr_byname_lazy( ctx.as_ptr(), self.as_ptr(), self.state.borrow().as_ptr(), - c_name.as_ptr(), + name.as_ref() + .into_c_ptr() + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)), ) }); match result { - Ok(inner) => Ok(Some(::from( - inner, - self.state.clone(), - ))), + Ok(inner) => Some(::from(inner, self.state.clone())), Err(NixideError::NixError { err: NixError::KeyNotFound(_), .. - }) => Ok(None), - Err(err) => Err(err), + }) => None, + Err(err) => panic_issue_call_failed!("{}", err), } } } diff --git a/nixide/src/flake/fetchers_settings.rs b/nixide/src/flake/fetchers_settings.rs index bfe56dd..dc6ed4e 100644 --- a/nixide/src/flake/fetchers_settings.rs +++ b/nixide/src/flake/fetchers_settings.rs @@ -1,8 +1,8 @@ -use std::ffi::{CString, c_char}; use std::ptr::NonNull; use crate::NixideResult; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::sys; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; @@ -11,6 +11,19 @@ pub struct FetchersSettings { inner: NonNull, } +// impl Clone for FetchersSettings { +// 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(), +// } +// } +// } + impl Drop for FetchersSettings { fn drop(&mut self) { unsafe { @@ -37,13 +50,12 @@ impl AsInnerPtr for FetchersSettings { } impl FetchersSettings { - pub fn new() -> Self { + pub fn new() -> NixideResult { let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_new(ctx.as_ptr()) - }) - .unwrap(); + })?; - Self { inner } + Ok(Self { inner }) } /// # Errors @@ -55,22 +67,22 @@ impl FetchersSettings { /// It is instead **exposed by the Nixide C API extensions.** /// #[allow(unused)] - pub fn add_access_token(&self, token_name: &str, token_value: &str) -> NixideResult<()> { + pub fn add_access_token(self, token_name: &str, token_value: &str) -> NixideResult { // XXX: TODO: have a dedicated `self.access_tokens: HashMap` instead - let c_name = CString::new(token_name).to_nixide_result()?; - let c_value = CString::new(token_value).to_nixide_result()?; + let name_ptr = token_name.into_c_ptr()?; + let value_ptr = token_value.into_c_ptr()?; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_add_access_token( ctx.as_ptr(), self.as_ptr(), - c_name.as_ptr() as *mut c_char, - c_value.as_ptr() as *mut c_char, + name_ptr, + value_ptr, ); }) .unwrap(); - Ok(()) + Ok(self) } /// # Errors @@ -82,20 +94,16 @@ impl FetchersSettings { /// It is instead **exposed by the Nixide C API extensions.** /// #[allow(unused)] - pub fn remove_access_token(&self, token_name: &str) -> NixideResult<()> { + pub fn remove_access_token(self, token_name: &str) -> NixideResult { // XXX: TODO: have a dedicated `self.access_tokens: HashMap` instead - let c_name = CString::new(token_name).to_nixide_result()?; + let name_ptr = token_name.into_c_ptr()?; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_fetchers_settings_remove_access_token( - ctx.as_ptr(), - self.as_ptr(), - c_name.as_ptr() as *mut c_char, - ); + sys::nix_fetchers_settings_remove_access_token(ctx.as_ptr(), self.as_ptr(), name_ptr); }) .unwrap(); - Ok(()) + Ok(self) } /// # Nix C API Internals @@ -103,11 +111,14 @@ impl FetchersSettings { /// This binding is **not provided by the Nix C API.** /// It is instead **exposed by the Nixide C API extensions.** /// - pub fn allow_dirty(&self, value: bool) { + #[allow(unused)] + pub fn allow_dirty(self, value: bool) -> Self { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_set_allow_dirty(ctx.as_ptr(), self.as_ptr(), value); }) .unwrap(); + + self } /// # Nix C API Internals @@ -115,11 +126,14 @@ impl FetchersSettings { /// This binding is **not provided by the Nix C API.** /// It is instead **exposed by the Nixide C API extensions.** /// - pub fn warn_dirty(&self, value: bool) { + #[allow(unused)] + pub fn warn_dirty(self, value: bool) -> Self { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_set_warn_dirty(ctx.as_ptr(), self.as_ptr(), value); }) .unwrap(); + + self } /// # Nix C API Internals @@ -127,11 +141,14 @@ impl FetchersSettings { /// This binding is **not provided by the Nix C API.** /// It is instead **exposed by the Nixide C API extensions.** /// - pub fn allow_dirty_locks(&self, value: bool) { + #[allow(unused)] + pub fn allow_dirty_locks(self, value: bool) -> Self { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_set_allow_dirty_locks(ctx.as_ptr(), self.as_ptr(), value); }) .unwrap(); + + self } /// # Nix C API Internals @@ -139,7 +156,8 @@ impl FetchersSettings { /// This binding is **not provided by the Nix C API.** /// It is instead **exposed by the Nixide C API extensions.** /// - pub fn trust_tarballs_from_git_forges(&self, value: bool) { + #[allow(unused)] + pub fn trust_tarballs_from_git_forges(self, value: bool) -> Self { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_set_trust_tarballs_from_git_forges( ctx.as_ptr(), @@ -148,6 +166,8 @@ impl FetchersSettings { ); }) .unwrap(); + + self } /// # Nix C API Internals @@ -155,15 +175,17 @@ impl FetchersSettings { /// This binding is **not provided by the Nix C API.** /// It is instead **exposed by the Nixide C API extensions.** /// - pub fn tarball_ttl(&self, ttl: u32) { + #[allow(unused)] + pub fn tarball_ttl(self, ttl: u32) -> Self { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_set_tarball_ttl(ctx.as_ptr(), self.as_ptr(), ttl); }) .unwrap(); + + self } /// # Errors - /// /// Fails if the given `registry` contains a NUL byte. /// /// # Nix C API Internals @@ -171,19 +193,20 @@ impl FetchersSettings { /// This binding is **not provided by the Nix C API.** /// It is instead **exposed by the Nixide C API extensions.** /// - pub fn global_flake_registry(&self, registry: &str) -> NixideResult<()> { - let c_registry = CString::new(registry).to_nixide_result()?; + #[allow(unused)] + pub fn global_flake_registry(self, registry: &str) -> NixideResult { + let registry_ptr = registry.into_c_ptr()?; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_fetchers_settings_set_global_flake_registry( ctx.as_ptr(), self.as_ptr(), - c_registry.as_ptr() as *mut c_char, + registry_ptr, ); }) .unwrap(); - Ok(()) + Ok(self) } } @@ -193,6 +216,6 @@ mod tests { #[test] fn fetchers_settings_new() { - let _ = FetchersSettings::new(); + let _ = FetchersSettings::new().unwrap(); } } diff --git a/nixide/src/flake/flake_lock_flags.rs b/nixide/src/flake/flake_lock_flags.rs index a1224e6..e9b1320 100644 --- a/nixide/src/flake/flake_lock_flags.rs +++ b/nixide/src/flake/flake_lock_flags.rs @@ -1,15 +1,16 @@ -use std::ffi::{CString, c_char}; -use std::ptr::{self, NonNull}; +use std::ffi::c_char; +use std::ptr::NonNull; -use super::FlakeRef; +use super::{FlakeRef, FlakeSettings}; use crate::NixideResult; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::sys; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; #[derive(Debug, Clone, Copy)] -pub enum LockMode { +pub enum FlakeLockMode { /// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file. /// /// This is the default mode. @@ -27,7 +28,7 @@ pub enum LockMode { /// Parameters that affect the locking of a flake. pub struct FlakeLockFlags { - inner: NonNull, + pub(crate) inner: NonNull, } // impl Clone for FlakeLockFlags { @@ -69,38 +70,12 @@ impl AsInnerPtr for FlakeLockFlags { } impl FlakeLockFlags { - pub fn new(mode: LockMode) -> Self { - // NOTE: `nix_flake_lock_flags_new` is a simple initialisation - // NOTE: and will only fail if out of memory (hence we can "safely" unwrap). + pub fn new(settings: &FlakeSettings) -> NixideResult { let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { - // NOTE: `nix_flake_lock_flags_new` currently never uses the `settings` - // NOTE: parameter, hence providing a null pointer is fine. - sys::nix_flake_lock_flags_new(ctx.as_ptr(), ptr::null_mut()) - }) - .unwrap(); + sys::nix_flake_lock_flags_new(ctx.as_ptr(), settings.as_ptr()) + })?; - let self_ = FlakeLockFlags { inner }; - - wrap::nix_fn!(|ctx: &ErrorContext| { - match mode { - LockMode::WriteAsNeeded => unsafe { - sys::nix_flake_lock_flags_set_mode_write_as_needed(ctx.as_ptr(), self_.as_ptr()) - }, - LockMode::Virtual => unsafe { - sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self_.as_ptr()) - }, - LockMode::Check => unsafe { - sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self_.as_ptr()) - }, - }; - }) - .unwrap(); - - self_ - } - - pub fn default() -> Self { - Self::new(LockMode::WriteAsNeeded) + Ok(FlakeLockFlags { inner }) } /// Adds an input override to the lock file that will be produced. @@ -119,13 +94,13 @@ impl FlakeLockFlags { #[allow(unused)] pub fn override_input(self, input: &str, flakeref: &FlakeRef) -> NixideResult { // XXX: TODO: should `input` be wrapped as `format!("inputs.{input}")`? - let c_input = CString::new(input).to_nixide_result()?; + 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(), - c_input.as_ptr(), + input_path, flakeref.as_ptr(), ); })?; @@ -147,14 +122,29 @@ impl FlakeLockFlags { #[allow(unused)] pub fn update_input(self, input: &str) -> NixideResult { // XXX: TODO: should `input` be wrapped as `format!("inputs.{input}")`? - let c_input = CString::new(input).to_nixide_result()?; + 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(), - c_input.as_ptr(), - ); + 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 { + FlakeLockMode::WriteAsNeeded => unsafe { + sys::nix_flake_lock_flags_set_mode_write_as_needed(ctx.as_ptr(), self.as_ptr()) + }, + FlakeLockMode::Virtual => unsafe { + sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self.as_ptr()) + }, + FlakeLockMode::Check => unsafe { + sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) + }, + }; })?; Ok(self) @@ -290,13 +280,13 @@ impl FlakeLockFlags { /// #[allow(unused)] pub fn input_lock_file_path(self, path: &str) -> NixideResult { - let c_path = CString::new(path).to_nixide_result()?; + let path_ptr = path.as_c_ptr()? as *mut c_char; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_flake_lock_flags_set_reference_lock_file_path( ctx.as_ptr(), self.as_ptr(), - c_path.as_ptr() as *mut c_char, + path_ptr, ) }) .unwrap(); @@ -314,13 +304,13 @@ impl FlakeLockFlags { /// #[allow(unused)] pub fn output_lock_file_path(self, path: &str) -> NixideResult { - let c_path = CString::new(path).to_nixide_result()?; + 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(), - c_path.as_ptr() as *mut c_char, + path_ptr, ) }) .unwrap(); diff --git a/nixide/src/flake/flake_settings.rs b/nixide/src/flake/flake_settings.rs index 1697ebb..3af309a 100644 --- a/nixide/src/flake/flake_settings.rs +++ b/nixide/src/flake/flake_settings.rs @@ -1,8 +1,8 @@ -use std::ffi::CString; use std::ptr::NonNull; use crate::NixideResult; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::sys; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; @@ -86,12 +86,12 @@ impl FlakeSettings { /// #[allow(unused)] pub fn commit_lock_file_summary(self, summary: &str) -> NixideResult { - let c_summary = CString::new(summary).to_nixide_result()?; + let summary_ptr = summary.into_c_ptr()?; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_flake_settings_set_commit_lock_file_summary( ctx.as_ptr(), self.as_ptr(), - c_summary.as_ptr().cast_mut(), + summary_ptr, ) }) .unwrap(); diff --git a/nixide/src/flake/flakeref.rs b/nixide/src/flake/flakeref.rs index 2c4e892..bfe44c5 100644 --- a/nixide/src/flake/flakeref.rs +++ b/nixide/src/flake/flakeref.rs @@ -1,13 +1,35 @@ -use std::ptr::NonNull; +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 { @@ -33,13 +55,45 @@ impl AsInnerPtr for FlakeRef { } } -// XXX: TODO: is it possible to get the URI string itself? (minus the fragment part?) impl FlakeRef { - #[inline] - pub(crate) fn new(inner: NonNull, fragment: String) -> Self { - Self { inner, fragment } + /// 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] diff --git a/nixide/src/flake/flakeref_builder.rs b/nixide/src/flake/flakeref_builder.rs deleted file mode 100644 index 13eecf2..0000000 --- a/nixide/src/flake/flakeref_builder.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::ffi::{CString, c_void}; -use std::path::Path; -use std::ptr::{self, NonNull, null_mut}; - -use super::{FetchersSettings, FlakeRef, FlakeRefParseFlags}; -use crate::errors::{ErrorContext, NixideResult, ToNixideResult as _, new_nixide_error}; -use crate::sys; -use crate::util::wrap; -use crate::util::wrappers::AsInnerPtr; - -pub struct FlakeRefBuilder { - reference: String, - fetch_settings: FetchersSettings, - parse_flags: FlakeRefParseFlags, -} - -impl FlakeRefBuilder { - /// 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 new>(reference: S) -> Self { - Self { - reference: reference.as_ref().to_string(), - fetch_settings: FetchersSettings::new(), - parse_flags: FlakeRefParseFlags::new(), - } - } - - pub fn build(&self) -> NixideResult { - let c_reference = CString::new(self.reference.as_str()).to_nixide_result()?; - - let mut ptr: *mut sys::NixFlakeReference = null_mut(); - let fragment = wrap::nix_string_callback!( - |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { - // NOTE: `nix_flake_reference_and_fragment_from_string` currently - // NOTE: never uses the `flake_settings` parameter, hence providing - // NOTE: a null pointer instead is fine. - sys::nix_flake_reference_and_fragment_from_string( - ctx.as_ptr(), - self.fetch_settings.as_ptr(), - ptr::null_mut(), - self.parse_flags.as_ptr(), - c_reference.as_ptr(), - self.reference.len(), - &mut ptr, - Some(callback), - userdata as *mut c_void, - ) - } - )?; - - match NonNull::new(ptr) { - Some(inner) => Ok(FlakeRef::new(inner, fragment)), - None => Err(new_nixide_error!(NullPtr)), - } - } - - pub fn base_directory>(mut self, path: P) -> NixideResult { - self.parse_flags - .base_directory(&path.as_ref().to_string_lossy())?; - - Ok(self) - } - - pub fn global_flake_registry(self, registry: &str) -> NixideResult { - self.fetch_settings.global_flake_registry(registry)?; - - Ok(self) - } - - pub fn allow_dirty(self, value: bool) -> Self { - self.fetch_settings.allow_dirty(value); - - self - } - - pub fn warn_dirty(self, value: bool) -> Self { - self.fetch_settings.warn_dirty(value); - - self - } - - pub fn allow_dirty_locks(self, value: bool) -> Self { - self.fetch_settings.allow_dirty_locks(value); - - self - } - - pub fn trust_tarballs_from_git_forges(self, value: bool) -> Self { - self.fetch_settings.trust_tarballs_from_git_forges(value); - - self - } - - pub fn tarball_ttl(self, ttl: u32) -> Self { - self.fetch_settings.tarball_ttl(ttl); - - self - } -} diff --git a/nixide/src/flake/flakeref_parse_flags.rs b/nixide/src/flake/flakeref_parse_flags.rs index 31dfdd6..f3bec84 100644 --- a/nixide/src/flake/flakeref_parse_flags.rs +++ b/nixide/src/flake/flakeref_parse_flags.rs @@ -1,29 +1,36 @@ -use std::ffi::CString; -use std::ptr::{self, NonNull}; +use std::ffi::c_char; +use std::ptr::NonNull; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use super::FlakeSettings; +use crate::NixideResult; +use crate::errors::ErrorContext; +use crate::sys; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; -use crate::{NixideResult, sys}; /// Parameters for parsing a flake reference. -/// -/// # Nix C API Internals -/// -/// Currently the Nix C API only uses this for storing -/// a base directory... Kinda overkill no? ;w; -/// #[derive(Debug)] -// pub(super) struct FlakeRefParseFlags { pub struct FlakeRefParseFlags { - // DEBUG (go back to pub(super) soon) inner: NonNull, } +// impl Clone for FlakeReferenceParseFlags { +// 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(), +// } +// } +// } + impl Drop for FlakeRefParseFlags { fn drop(&mut self) { unsafe { - sys::nix_flake_reference_parse_flags_free(self.as_ptr()); + sys::nix_flake_reference_parse_flags_free(self.inner.as_ptr()); } } } @@ -46,40 +53,24 @@ impl AsInnerPtr for FlakeRefParseFlags { } impl FlakeRefParseFlags { - pub fn new() -> Self { - // NOTE: `nix_flake_reference_parse_flags_new` is a simple initialisation - // NOTE: and will only fail if out of memory (hence we can "safely" unwrap). + pub fn new(settings: &FlakeSettings) -> NixideResult { let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { - // NOTE: `nix_flake_reference_parse_flags_new` currently never uses the `settings` - // NOTE: parameter, hence providing a null pointer is fine. - sys::nix_flake_reference_parse_flags_new(ctx.as_ptr(), ptr::null_mut()) - }) - .unwrap(); + sys::nix_flake_reference_parse_flags_new(ctx.as_ptr(), settings.as_ptr()) + })?; - Self { inner } + Ok(Self { inner }) } /// Sets the [base directory](https://nix.dev/manual/nix/latest/glossary#gloss-base-directory) /// for resolving local flake references. - /// - /// # Nix C API Internals - /// - /// If `set_base_directory` is never called, the base directory defaults - /// to the current working directory (see `libutil/filesystem.cc:absPath`). - /// - pub fn base_directory(&mut self, base_directory: &str) -> NixideResult<()> { - let c_base_directory = CString::new(base_directory).to_nixide_result()?; - + pub fn set_base_directory(&mut self, base_directory: &str) -> NixideResult<()> { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_flake_reference_parse_flags_set_base_directory( ctx.as_ptr(), self.as_ptr(), - c_base_directory.as_ptr(), + base_directory.as_ptr() as *const c_char, base_directory.len(), ); }) - .unwrap(); - - Ok(()) } } diff --git a/nixide/src/flake/locked_flake.rs b/nixide/src/flake/locked_flake.rs index b0be32e..7a71fa6 100644 --- a/nixide/src/flake/locked_flake.rs +++ b/nixide/src/flake/locked_flake.rs @@ -1,8 +1,10 @@ +// XXX: TODO: find a way to read directly from FlakeSettings and FetchersSettings (the C++ classes) + use std::cell::RefCell; -use std::ptr::{self, NonNull}; +use std::ptr::NonNull; use std::rc::Rc; -use super::{FlakeLockFlags, FlakeRef, FlakeRefBuilder, FlakeSettings}; +use super::{FetchersSettings, FlakeLockFlags, FlakeLockMode, FlakeRef, FlakeSettings}; use crate::errors::ErrorContext; use crate::sys; use crate::util::wrap; @@ -15,6 +17,7 @@ pub struct LockedFlake { flakeref: FlakeRef, state: Rc>>, lock_flags: FlakeLockFlags, + fetch_settings: FetchersSettings, flake_settings: FlakeSettings, } @@ -45,19 +48,19 @@ impl AsInnerPtr for LockedFlake { impl LockedFlake { pub fn lock( - lock_flags: FlakeLockFlags, + mode: FlakeLockMode, flakeref: FlakeRef, state: &EvalState, ) -> NixideResult { let state_inner = state.inner_ref(); + let fetch_settings = FetchersSettings::new()?; let flake_settings = FlakeSettings::new()?; + let lock_flags = FlakeLockFlags::new(&flake_settings)?.set_mode(mode)?; let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { - // NOTE: `nix_flake_lock` currently never uses the `fetchSettings` - // NOTE: parameter, hence providing a null pointer instead is fine. sys::nix_flake_lock( ctx.as_ptr(), - ptr::null_mut(), + fetch_settings.as_ptr(), flake_settings.as_ptr(), state_inner.borrow().as_ptr(), lock_flags.as_ptr(), @@ -70,6 +73,7 @@ impl LockedFlake { flakeref, state: state_inner.clone(), lock_flags, + fetch_settings, flake_settings, }) } @@ -94,8 +98,8 @@ mod tests { use std::fs; use std::sync::Once; - use super::{FlakeLockFlags, FlakeRefBuilder, FlakeSettings, LockedFlake}; - use crate::flake::LockMode; + use super::{FetchersSettings, FlakeLockFlags, FlakeRef, FlakeSettings, LockedFlake}; + use crate::flake::{FlakeLockMode, FlakeRefParseFlags}; use crate::{EvalStateBuilder, Store, Value, set_global_setting}; static INIT: Once = Once::new(); @@ -157,16 +161,12 @@ mod tests { .build() .unwrap(); - // let flakeref = FlakeRefBuilder::new(format!("path:{}#subthing", tmp_dir.path().display())) // DEBUG - let flakeref = FlakeRefBuilder::new(format!("{}#subthing", tmp_dir.path().display())) - .build() - .unwrap(); + let flakeref = + FlakeRef::parse(&format!("path:{}#subthing", tmp_dir.path().display())).unwrap(); assert_eq!(flakeref.fragment(), "subthing"); - let flake_lock_flags = FlakeLockFlags::new(LockMode::WriteAsNeeded); - - let outputs = LockedFlake::lock(flake_lock_flags, flakeref, &eval_state) + let outputs = LockedFlake::lock(FlakeLockMode::WriteAsNeeded, flakeref, &eval_state) .unwrap() .outputs() .unwrap(); @@ -175,203 +175,215 @@ mod tests { if let Value::Attrs(outputs) = outputs { let value = outputs.get("hello").unwrap(); - assert!(matches!(value, Some(Value::String(_)))); - if let Some(Value::String(value)) = value { + assert!(matches!(value, Value::String(_))); + if let Value::String(value) = value { assert_eq!(value.as_string(), "world"); } } } - // #[test] # DEBUG (too lazy to keep fixing this function...) - // fn flake_lock_load_flake_with_flags() { - // init(); + #[test] + fn flake_lock_load_flake_with_flags() { + init(); - // let store_ref = Store::default().unwrap(); - // let flake_settings = FlakeSettings::new().unwrap(); - // let eval_state = EvalStateBuilder::new(store_ref.clone()) - // .unwrap() - // .set_flake_settings(&flake_settings) - // .unwrap() - // .build() - // .unwrap(); + let store_ref = Store::default().unwrap(); + let fetchers_settings = FetchersSettings::new().unwrap(); + let flake_settings = FlakeSettings::new().unwrap(); + let eval_state = EvalStateBuilder::new(store_ref.clone()) + .unwrap() + .set_flake_settings(&flake_settings) + .unwrap() + .build() + .unwrap(); - // let tmp_dir = tempfile::tempdir().unwrap(); + let tmp_dir = tempfile::tempdir().unwrap(); - // let flake_dir_a = tmp_dir.path().join("a"); - // let flake_dir_b = tmp_dir.path().join("b"); - // let flake_dir_c = tmp_dir.path().join("c"); + let flake_dir_a = tmp_dir.path().join("a"); + let flake_dir_b = tmp_dir.path().join("b"); + let flake_dir_c = tmp_dir.path().join("c"); - // std::fs::create_dir_all(&flake_dir_a).unwrap(); - // std::fs::create_dir_all(&flake_dir_b).unwrap(); - // std::fs::create_dir_all(&flake_dir_c).unwrap(); + std::fs::create_dir_all(&flake_dir_a).unwrap(); + std::fs::create_dir_all(&flake_dir_b).unwrap(); + std::fs::create_dir_all(&flake_dir_c).unwrap(); - // let flake_dir_a_str = flake_dir_a.to_str().unwrap(); - // let flake_dir_c_str = flake_dir_c.to_str().unwrap(); - // assert!(!flake_dir_a_str.is_empty()); - // assert!(!flake_dir_c_str.is_empty()); + let flake_dir_a_str = flake_dir_a.to_str().unwrap(); + let flake_dir_c_str = flake_dir_c.to_str().unwrap(); + assert!(!flake_dir_a_str.is_empty()); + assert!(!flake_dir_c_str.is_empty()); - // // a - // std::fs::write( - // tmp_dir.path().join("a/flake.nix"), - // r#" - // { - // inputs.b.url = "@flake_dir_b@"; - // outputs = { b, ... }: { - // hello = b.hello; - // }; - // } - // "# - // .replace("@flake_dir_b@", flake_dir_b.to_str().unwrap()), - // ) - // .unwrap(); + // a + std::fs::write( + tmp_dir.path().join("a/flake.nix"), + r#" + { + inputs.b.url = "@flake_dir_b@"; + outputs = { b, ... }: { + hello = b.hello; + }; + } + "# + .replace("@flake_dir_b@", flake_dir_b.to_str().unwrap()), + ) + .unwrap(); - // // b - // std::fs::write( - // tmp_dir.path().join("b/flake.nix"), - // r#" - // { - // outputs = { ... }: { - // hello = "ALICE"; - // }; - // } - // "#, - // ) - // .unwrap(); + // b + std::fs::write( + tmp_dir.path().join("b/flake.nix"), + r#" + { + outputs = { ... }: { + hello = "ALICE"; + }; + } + "#, + ) + .unwrap(); - // // c - // std::fs::write( - // tmp_dir.path().join("c/flake.nix"), - // r#" - // { - // outputs = { ... }: { - // hello = "Claire"; - // }; - // } - // "#, - // ) - // .unwrap(); + // c + std::fs::write( + tmp_dir.path().join("c/flake.nix"), + r#" + { + outputs = { ... }: { + hello = "Claire"; + }; + } + "#, + ) + .unwrap(); - // let flakeref_a = FlakeRefBuilder::new(format!("path:{}", &flake_dir_a_str)) - // .build() - // .unwrap(); + let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); - // assert_eq!(flakeref_a.fragment(), ""); + let mut flake_reference_parse_flags = FlakeRefParseFlags::new(&flake_settings).unwrap(); - // // Step 1: Do not update (check), fails - // let mut flake_lock_flags = FlakeLockFlags::new(LockMode::Check); + flake_reference_parse_flags + .set_base_directory(tmp_dir.path().to_str().unwrap()) + .unwrap(); - // let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state); - // // Has not been locked and would need to write a lock file. - // assert!(locked_flake.is_err()); - // let saved_err = match locked_flake { - // Ok(_) => panic!("Expected error, but got Ok"), - // Err(e) => e, - // }; + let flakeref_a = FlakeRef::parse(&format!("path:{}", &flake_dir_a_str)).unwrap(); - // // Step 2: Update but do not write, succeeds - // let flake_lock_flags = FlakeLockFlags::new(LockMode::Virtual); + assert_eq!(flakeref_a.fragment(), ""); - // let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); + // Step 1: Do not update (check), fails + flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap(); - // let outputs = locked_flake.outputs().unwrap(); + let locked_flake = LockedFlake::lock(FlakeLockMode::Check, flakeref_a, &eval_state); + // Has not been locked and would need to write a lock file. + assert!(locked_flake.is_err()); + let saved_err = match locked_flake { + Ok(_) => panic!("Expected error, but got Ok"), + Err(e) => e, + }; - // assert!(matches!(outputs, Value::Attrs(_))); - // if let Value::Attrs(outputs) = outputs { - // let value = outputs.get("hello").unwrap(); + // Step 2: Update but do not write, succeeds + flake_lock_flags.set_mode(FlakeLockMode::Virtual).unwrap(); - // assert!(matches!(value, Value::String(_))); - // if let Value::String(value) = value { - // assert_eq!(value.as_string(), "ALICE"); - // } - // } + let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); - // // Step 3: The lock was not written, so Step 1 would fail again - // let flake_lock_flags = FlakeLockFlags::new(LockMode::Check); + let outputs = locked_flake.outputs().unwrap(); - // let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state); - // // Has not been locked and would need to write a lock file. - // match locked_flake { - // Ok(_) => panic!("Expected error, but got Ok"), - // Err(e) => { - // assert_eq!(e.to_string(), saved_err.to_string()); - // }, - // }; + assert!(matches!(outputs, Value::Attrs(_))); + if let Value::Attrs(outputs) = outputs { + let value = outputs.get("hello").unwrap(); - // // Step 4: Update and write, succeeds - // let flake_lock_flags = FlakeLockFlags::new(LockMode::WriteAsNeeded); + assert!(matches!(value, Value::String(_))); + if let Value::String(value) = value { + assert_eq!(value.as_string(), "ALICE"); + } + } - // let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); + // Step 3: The lock was not written, so Step 1 would fail again + flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap(); - // let outputs = locked_flake.outputs().unwrap(); + let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state); + // Has not been locked and would need to write a lock file. + match locked_flake { + Ok(_) => panic!("Expected error, but got Ok"), + Err(e) => { + assert_eq!(e.to_string(), saved_err.to_string()); + }, + }; - // assert!(matches!(outputs, Value::Attrs(_))); - // if let Value::Attrs(outputs) = outputs { - // let value = outputs.get("hello").unwrap(); + // Step 4: Update and write, succeeds + flake_lock_flags + .set_mode(FlakeLockMode::WriteAsNeeded) + .unwrap(); - // assert!(matches!(value, Value::String(_))); - // if let Value::String(value) = value { - // assert_eq!(value.as_string(), "ALICE"); - // } - // } + let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); - // // Step 5: Lock was written, so Step 1 succeeds - // let flake_lock_flags = FlakeLockFlags::new(LockMode::Check); + let outputs = locked_flake.outputs().unwrap(); - // let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); + assert!(matches!(outputs, Value::Attrs(_))); + if let Value::Attrs(outputs) = outputs { + let value = outputs.get("hello").unwrap(); - // let outputs = locked_flake.outputs().unwrap(); + assert!(matches!(value, Value::String(_))); + if let Value::String(value) = value { + assert_eq!(value.as_string(), "ALICE"); + } + } - // assert!(matches!(outputs, Value::Attrs(_))); - // if let Value::Attrs(outputs) = outputs { - // let value = outputs.get("hello").unwrap(); + // Step 5: Lock was written, so Step 1 succeeds + flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap(); - // assert!(matches!(value, Value::String(_))); - // if let Value::String(value) = value { - // assert_eq!(value.as_string(), "ALICE"); - // } - // } + let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); - // // Step 6: Lock with override, do not write + let outputs = locked_flake.outputs().unwrap(); - // // This shouldn't matter; write_as_needed will be overridden - // let flake_lock_flags = FlakeLockFlags::new(LockMode::WriteAsNeeded); + assert!(matches!(outputs, Value::Attrs(_))); + if let Value::Attrs(outputs) = outputs { + let value = outputs.get("hello").unwrap(); - // let flakeref_c = FlakeRefBuilder::new(format!("path:{}", &flake_dir_c_str)) - // .build() - // .unwrap(); - // assert_eq!(flakeref_c.fragment(), ""); + assert!(matches!(value, Value::String(_))); + if let Value::String(value) = value { + assert_eq!(value.as_string(), "ALICE"); + } + } - // flake_lock_flags.override_input("b", &flakeref_c).unwrap(); + // Step 6: Lock with override, do not write - // let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); + // This shouldn't matter; write_as_needed will be overridden + flake_lock_flags + .set_mode(FlakeLockMode::WriteAsNeeded) + .unwrap(); - // let outputs = locked_flake.outputs().unwrap(); + let flakeref_c = FlakeRef::parse(&format!("path:{}", &flake_dir_c_str)).unwrap(); + assert_eq!(flakeref_c.fragment(), ""); - // assert!(matches!(outputs, Value::Attrs(_))); - // if let Value::Attrs(outputs) = outputs { - // let value = outputs.get("hello").unwrap(); + flake_lock_flags.override_input("b", &flakeref_c).unwrap(); - // assert!(matches!(value, Value::String(_))); - // if let Value::String(value) = value { - // assert_eq!(value.as_string(), "Claire"); - // } - // } + let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); - // // Step 7: Override was not written; lock still points to b - // let flake_lock_flags = FlakeLockFlags::new(LockMode::Check); + let outputs = locked_flake.outputs().unwrap(); - // let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); + assert!(matches!(outputs, Value::Attrs(_))); + if let Value::Attrs(outputs) = outputs { + let value = outputs.get("hello").unwrap(); - // let outputs = locked_flake.outputs().unwrap(); + assert!(matches!(value, Value::String(_))); + if let Value::String(value) = value { + assert_eq!(value.as_string(), "Claire"); + } + } - // assert!(matches!(outputs, Value::Attrs(_))); - // if let Value::Attrs(outputs) = outputs { - // let value = outputs.get("hello").unwrap(); + // Can't delete overrides, so trash it + let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap(); - // assert!(matches!(value, Value::String(_))); - // if let Value::String(value) = value { - // assert_eq!(value.as_string(), "ALICE"); - // } - // } - // } + // Step 7: Override was not written; lock still points to b + flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap(); + + let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap(); + + let outputs = locked_flake.outputs().unwrap(); + + assert!(matches!(outputs, Value::Attrs(_))); + if let Value::Attrs(outputs) = outputs { + let value = outputs.get("hello").unwrap(); + + assert!(matches!(value, Value::String(_))); + if let Value::String(value) = value { + assert_eq!(value.as_string(), "ALICE"); + } + } + } } diff --git a/nixide/src/flake/mod.rs b/nixide/src/flake/mod.rs index 3cbf8c9..68dfad2 100644 --- a/nixide/src/flake/mod.rs +++ b/nixide/src/flake/mod.rs @@ -2,14 +2,12 @@ mod fetchers_settings; mod flake_lock_flags; mod flake_settings; mod flakeref; -mod flakeref_builder; mod flakeref_parse_flags; mod locked_flake; -pub use fetchers_settings::FetchersSettings; -pub use flake_lock_flags::{FlakeLockFlags, LockMode}; +use fetchers_settings::FetchersSettings; +use flake_lock_flags::{FlakeLockFlags, FlakeLockMode}; pub use flake_settings::FlakeSettings; -pub use flakeref::FlakeRef; -pub use flakeref_builder::FlakeRefBuilder; -pub use flakeref_parse_flags::FlakeRefParseFlags; +use flakeref::FlakeRef; +use flakeref_parse_flags::FlakeRefParseFlags; pub use locked_flake::LockedFlake; diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index ad9e221..fdb79ac 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -29,6 +29,6 @@ pub use version::NixVersion; #[cfg(feature = "exprs")] pub use expr::{EvalState, EvalStateBuilder, Value}; #[cfg(feature = "flakes")] -pub use flake::{FlakeRef, FlakeRefBuilder, FlakeSettings, LockedFlake}; +pub use flake::{FlakeSettings, LockedFlake}; #[cfg(feature = "store")] pub use store::{Store, StorePath}; diff --git a/nixide/src/logging.rs b/nixide/src/logging.rs index 495226a..87386e5 100644 --- a/nixide/src/logging.rs +++ b/nixide/src/logging.rs @@ -1,6 +1,5 @@ -use std::ffi::CString; - -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::sys; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr as _; @@ -70,10 +69,8 @@ impl ToString for LogFormat { /// modified without nixide updating to account for this. /// pub fn set_log_format(format: LogFormat) { - let c_format = CString::new(format.to_string()).to_nixide_result().unwrap(); - wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_set_log_format(ctx.as_ptr(), c_format.as_ptr()); + sys::nix_set_log_format(ctx.as_ptr(), format.to_string().as_c_ptr().unwrap()); }) .unwrap() } diff --git a/nixide/src/nix_settings.rs b/nixide/src/nix_settings.rs index 166eb63..21dd9a3 100644 --- a/nixide/src/nix_settings.rs +++ b/nixide/src/nix_settings.rs @@ -1,26 +1,21 @@ -use std::ffi::{CString, c_void}; +use std::ffi::c_void; use crate::NixideResult; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr as _; -// DEBUG: should this really be unsafe? /// # Note /// This function is intentionally marked unsafe to discourage its use. /// Please prefer [nixide::FlakeSettings] and [nixide::FetchersSettings]. /// -pub unsafe fn get_global_setting(key: &str) -> NixideResult { - let c_key = CString::new(key).to_nixide_result()?; +pub unsafe fn get_global_setting>(key: S) -> NixideResult { + let key = key.as_c_ptr()?; wrap::nix_string_callback!( |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { - sys::nix_setting_get( - ctx.as_ptr(), - c_key.as_ptr(), - Some(callback), - userdata as *mut c_void, - ); + sys::nix_setting_get(ctx.as_ptr(), key, Some(callback), userdata as *mut c_void); } ) } @@ -29,11 +24,14 @@ pub unsafe fn get_global_setting(key: &str) -> NixideResult { /// This function is intentionally marked unsafe to discourage its use. /// Please prefer [nixide::FlakeSettings] and [nixide::FetchersSettings]. /// -pub unsafe fn set_global_setting(key: &str, value: &str) -> NixideResult<()> { - let c_key = CString::new(key).to_nixide_result()?; - let c_value = CString::new(value).to_nixide_result()?; +pub unsafe fn set_global_setting, T: AsRef>( + key: S, + value: T, +) -> NixideResult<()> { + let key = key.as_c_ptr()?; + let value = value.as_c_ptr()?; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_setting_set(ctx.as_ptr(), c_key.as_ptr(), c_value.as_ptr()); + sys::nix_setting_set(ctx.as_ptr(), key, value); }) } diff --git a/nixide/src/plugins.rs b/nixide/src/plugins.rs index 00ca391..f9f7e8c 100644 --- a/nixide/src/plugins.rs +++ b/nixide/src/plugins.rs @@ -1,7 +1,6 @@ -use std::ffi::CString; - use crate::NixideResult; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr as _; @@ -12,8 +11,8 @@ pub fn load_plugins() -> NixideResult<()> { } pub fn register_plugin>(path: S) -> NixideResult<()> { - let c_path = CString::new(path.as_ref()).to_nixide_result()?; + let path_ptr = path.as_ref().into_c_ptr()?; wrap::nix_fn!(|ctx: &ErrorContext| unsafe { - sys::nix_register_plugin(ctx.as_ptr(), c_path.as_ptr().cast_mut()); + sys::nix_register_plugin(ctx.as_ptr(), path_ptr); }) } diff --git a/nixide/src/stdext/cchar_ptr.rs b/nixide/src/stdext/cchar_ptr.rs index b80d08c..bf1840e 100644 --- a/nixide/src/stdext/cchar_ptr.rs +++ b/nixide/src/stdext/cchar_ptr.rs @@ -5,6 +5,33 @@ use std::str::from_utf8; use crate::NixideResult; use crate::errors::new_nixide_error; +pub trait AsCPtr { + #[allow(unused)] + fn as_c_ptr(&self) -> NixideResult<*const T>; + + #[allow(unused)] + fn into_c_ptr(self) -> NixideResult<*mut T>; +} + +impl AsCPtr for T +where + T: AsRef, +{ + fn as_c_ptr(&self) -> NixideResult<*const c_char> { + match CStr::from_bytes_until_nul(self.as_ref().as_bytes()) { + Ok(s) => Ok(s.as_ptr()), + Err(_) => Err(new_nixide_error!(StringNulByte)), + } + } + + fn into_c_ptr(self) -> NixideResult<*mut c_char> { + match CStr::from_bytes_until_nul(self.as_ref().as_bytes()) { + Ok(s) => Ok(s.as_ptr().cast_mut()), + Err(_) => Err(new_nixide_error!(StringNulByte)), + } + } +} + pub trait CCharPtrExt { #[allow(unused)] fn to_utf8_string(self) -> NixideResult; diff --git a/nixide/src/stdext/mod.rs b/nixide/src/stdext/mod.rs index e893a29..cdf4416 100644 --- a/nixide/src/stdext/mod.rs +++ b/nixide/src/stdext/mod.rs @@ -1,5 +1,5 @@ mod cchar_ptr; mod slice; -pub(crate) use cchar_ptr::CCharPtrExt; +pub(crate) use cchar_ptr::{AsCPtr, CCharPtrExt}; pub(crate) use slice::SliceExt; diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index b193109..bb0d398 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -8,13 +8,14 @@ mod path; pub use path::*; use std::cell::RefCell; -use std::ffi::{CString, c_char, c_void}; +use std::ffi::{c_char, c_void}; use std::path::PathBuf; use std::ptr::{NonNull, null, null_mut}; use std::rc::Rc; use crate::NixideResult; -use crate::errors::{ErrorContext, ToNixideResult as _}; +use crate::errors::ErrorContext; +use crate::stdext::AsCPtr as _; use crate::sys; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; @@ -57,8 +58,7 @@ impl Store { /// Returns an error if the store cannot be opened. /// pub fn open(uri: &str) -> NixideResult>> { - let c_uri = CString::new(uri).to_nixide_result()?; - unsafe { Self::open_ptr(c_uri.as_ptr()) } + unsafe { Self::open_ptr(uri.as_c_ptr()?) } } /// Opens a connection to the default Nix store. diff --git a/nixide/src/store/path.rs b/nixide/src/store/path.rs index b316997..5ed68d3 100644 --- a/nixide/src/store/path.rs +++ b/nixide/src/store/path.rs @@ -79,8 +79,8 @@ impl StorePath { /// use nixide::{Store, StorePath}; /// /// fn main() { - /// let storeref = Store::default().unwrap(); - /// let path = StorePath::new(storeref, "/nix/store/f7gmvzd74wc1vlxzjdqy0af2381g8wr6-nix-manual-2.31.2-man").unwrap(); + /// let store_ref = Store::default().unwrap(); + /// let path = StorePath::new("/nix/store/f7gmvzd74wc1vlxzjdqy0af2381g8wr6-nix-manual-2.31.2-man").unwrap(); /// } /// ``` ///