fix bad usage of sys::nix_gc_incref

This commit is contained in:
do butterflies cry? 2026-03-30 12:48:43 +10:00
parent 53c2d40393
commit fee35fa88b
Signed by: cry
GPG key ID: F68745A836CA0412
32 changed files with 1359 additions and 682 deletions

356
Cargo.lock generated
View file

@ -11,6 +11,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "bindgen"
version = "0.72.1"
@ -118,12 +124,40 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures-core"
version = "0.3.32"
@ -159,12 +193,64 @@ dependencies = [
"slab",
]
[[package]]
name = "getrandom"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
"wasip3",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "indexmap"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
[[package]]
name = "itertools"
version = "0.13.0"
@ -174,6 +260,18 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.183"
@ -190,6 +288,12 @@ dependencies = [
"windows-link",
]
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "lock_api"
version = "0.4.14"
@ -226,6 +330,7 @@ dependencies = [
"nixide-sys",
"serial_test",
"stdext",
"tempfile",
]
[[package]]
@ -290,6 +395,16 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@ -308,6 +423,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "redox_syscall"
version = "0.5.18"
@ -352,6 +473,19 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "scc"
version = "2.4.0"
@ -373,6 +507,54 @@ version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca"
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serial_test"
version = "3.4.0"
@ -434,20 +616,194 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "yap"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe269e7b803a5e8e20cbd97860e136529cd83bf2c9c6d37b142467e7e1f051f"
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View file

@ -23,6 +23,7 @@ libc = "0.2.183"
stdext = "0.3.3"
ctor = "0.6.3"
nixide-sys = { path = "../nixide-sys", version = "0.1.0", features = ["nix-util-c", "nix-main-c"]}
tempfile = "3.27.0"
[dev-dependencies]
serial_test = "3.4.0"

View file

@ -10,14 +10,14 @@
// * These may be rendered differently, so that users can distinguish them.
// */
// bool isFromExpr = false;
//
// /**
// * Exit status.
// */
// unsigned int status = 1;
//
// Suggestions suggestions;
//
// static std::optional<std::string> programName;
// };
@ -67,6 +67,19 @@ pub(crate) struct ErrorContext {
inner: NonNull<sys::nix_c_context>,
}
// impl Clone for ErrorContext {
// 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<sys::nix_c_context> for ErrorContext {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_c_context {
@ -183,7 +196,6 @@ impl ErrorContext {
/// nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg)
/// {
/// if (context == nullptr) {
/// // todo last_err_code
/// throw nix::Error("Nix C api error: %s", msg);
/// }
/// context->last_err_code = err;
@ -218,11 +230,9 @@ impl ErrorContext {
/// This function **never fails**.
///
fn get_err(&self) -> Option<sys::nix_err> {
let err = unsafe { sys::nix_err_code(self.as_ptr()) };
match err {
match unsafe { sys::nix_err_code(self.as_ptr()) } {
sys::nix_err_NIX_OK => None,
_ => Some(err),
err => Some(err),
}
}
@ -257,13 +267,17 @@ impl ErrorContext {
/// and avoid passing in a [sys::nix_c_context] struct.
///
fn get_msg(&self) -> Option<String> {
let ctx = ErrorContext::new();
unsafe {
// NOTE: an Err here only occurs when `self.get_code() == Ok(())`
let mut n: c_uint = 0;
// 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 {
sys::nix_err_msg(ctx.as_ptr(), self.as_ptr(), &mut n)
.to_utf8_string_n(n as usize)
.ok()
});
match result {
Ok(option) => option,
Err(_) => None,
}
}
@ -305,19 +319,18 @@ impl ErrorContext {
/// ```
///
fn get_nix_err_name(&self) -> Option<String> {
#[allow(unused_unsafe)] // XXX: TODO: remove this `unused_unsafe`
unsafe {
// NOTE: an Err here only occurs when "Last error was not a nix error"
wrap::nix_string_callback!(|callback, userdata: *mut __UserData, ctx: &ErrorContext| {
// NOTE: an Err here only occurs when "Last error was not a nix error"
wrap::nix_string_callback!(
|callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe {
sys::nix_err_name(
ctx.as_ptr(),
self.as_ptr(),
Some(callback),
userdata as *mut c_void,
)
})
.ok()
}
}
)
.ok()
}
/// Returns [None] if [self.code] is [sys::nix_err_NIX_OK], and [Some] otherwise.
@ -358,19 +371,16 @@ impl ErrorContext {
/// ```
///
fn get_nix_err_info_msg(&self) -> Option<String> {
#[allow(unused_unsafe)] // XXX: TODO: remove this `unused_unsafe`
unsafe {
// NOTE: an Err here only occurs when "Last error was not a nix error"
wrap::nix_string_callback!(|callback, userdata, ctx: &ErrorContext| {
sys::nix_err_info_msg(
ctx.as_ptr(),
self.as_ptr(),
Some(callback),
userdata as *mut c_void,
)
})
.ok()
}
// NOTE: an Err here only occurs when "Last error was not a nix error"
wrap::nix_string_callback!(|callback, userdata, ctx: &ErrorContext| unsafe {
sys::nix_err_info_msg(
ctx.as_ptr(),
self.as_ptr(),
Some(callback),
userdata as *mut c_void,
)
})
.ok()
}
}

View file

@ -1,8 +1,5 @@
use std::cell::RefCell;
use std::ffi::CString;
use std::ffi::{CString, c_void};
use std::ptr::NonNull;
use std::rc::Rc;
use std::sync::Arc;
use crate::errors::new_nixide_error;
@ -18,46 +15,56 @@ use crate::{NixideResult, Store};
/// This provides the main interface for evaluating Nix expressions
/// and creating values.
pub struct EvalState {
inner: Rc<RefCell<NonNull<sys::EvalState>>>,
inner: NonNull<sys::EvalState>,
// XXX: TODO: is an `Arc<Store>` necessary or just a `Store`
store: Arc<Store>,
store: Store,
}
// 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<sys::EvalState> for EvalState {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::EvalState {
self.inner.borrow().as_ptr()
self.inner.as_ptr()
}
#[inline]
unsafe fn as_ref(&self) -> &sys::EvalState {
unsafe { self.inner.borrow().as_ref() }
unsafe { self.inner.as_ref() }
}
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::EvalState {
unsafe { self.inner.borrow_mut().as_mut() }
unsafe { self.inner.as_mut() }
}
}
impl EvalState {
/// Construct a new EvalState directly from its attributes
pub(super) fn new(inner: NonNull<sys::EvalState>, store: Arc<Store>) -> Self {
///
pub(super) fn new(inner: NonNull<sys::EvalState>, store: &Store) -> Self {
Self {
inner: Rc::new(RefCell::new(inner)),
store,
inner,
store: store.clone(),
}
}
#[inline]
pub(crate) fn inner_ref(&self) -> Rc<RefCell<NonNull<sys::EvalState>>> {
self.inner.clone()
}
#[inline]
pub(crate) unsafe fn store_ref(&self) -> &Store {
self.store.as_ref()
pub fn store_ref(&self) -> &Store {
&self.store
}
/// Evaluate a Nix expression from a string.
@ -70,7 +77,7 @@ impl EvalState {
/// # Errors
///
/// Returns an error if evaluation fails.
pub fn eval_from_string(&self, expr: &str, path: &str) -> NixideResult<Value> {
pub fn interpret(&self, expr: &str, path: &str) -> NixideResult<Value> {
let expr_c = CString::new(expr).or(Err(new_nixide_error!(StringNulByte)))?;
let path_c = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?;
@ -92,7 +99,7 @@ impl EvalState {
);
value
})
.map(|ptr| Value::from((ptr, self.inner_ref())))
.map(|ptr| Value::from((ptr, self)))
}
}

View file

@ -1,6 +1,5 @@
use std::ffi::{CString, c_char};
use std::ffi::{CString, c_char, c_void};
use std::ptr::{self, NonNull};
use std::sync::Arc;
use super::EvalState;
#[cfg(feature = "flakes")]
@ -15,11 +14,28 @@ use crate::util::{panic_issue_call_failed, wrap};
///
/// This allows configuring the evaluation environment before creating
/// the evaluation state.
///
pub struct EvalStateBuilder {
inner: NonNull<sys::nix_eval_state_builder>,
store: Arc<Store>,
store: Store,
}
// impl Clone for EvalStateBuilder {
// 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<sys::nix_eval_state_builder> for EvalStateBuilder {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder {
@ -47,17 +63,15 @@ impl EvalStateBuilder {
/// # Errors
///
/// Returns an error if the builder cannot be created.
pub fn new(store: &Arc<Store>) -> NixideResult<Self> {
///
pub fn new(store: &Store) -> NixideResult<Self> {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_eval_state_builder_new(ctx.as_ptr(), store.as_ptr())
})?;
// sys::nix_eval_state_builder_load(context, builder);
// sys::nix_flake_settings_add_to_eval_state_builder(context, settings, builder);
Ok(EvalStateBuilder {
inner,
store: Arc::clone(store),
store: store.clone(),
})
}
@ -66,18 +80,20 @@ impl EvalStateBuilder {
/// # Errors
///
/// Returns an error if the evaluation state cannot be built.
///
pub fn build(self) -> NixideResult<EvalState> {
// Build the state
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_eval_state_build(ctx.as_ptr(), self.as_ptr())
})?;
Ok(EvalState::new(inner, self.store.clone()))
Ok(EvalState::new(inner, &self.store))
}
// XXX: TODO
// XXX: TODO: use `flakes()` instead
#[deprecated]
#[cfg(feature = "flakes")]
fn set_flake_settings(self, settings: FlakeSettings) -> NixideResult<Self> {
pub fn set_flake_settings(self, settings: &FlakeSettings) -> NixideResult<Self> {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_flake_settings_add_to_eval_state_builder(
ctx.as_ptr(),
@ -89,7 +105,13 @@ impl EvalStateBuilder {
Ok(self)
}
fn load_ambient_settings(self) -> NixideResult<Self> {
#[cfg(feature = "flakes")]
pub fn flakes(self) -> NixideResult<Self> {
#[allow(deprecated)]
self.set_flake_settings(&FlakeSettings::new()?)
}
pub fn load_ambient_settings(self) -> NixideResult<Self> {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_eval_state_builder_load(ctx.as_ptr(), self.as_ptr());
})?;
@ -97,7 +119,7 @@ impl EvalStateBuilder {
Ok(self)
}
fn set_lookup_path<P: AsRef<str>>(self, paths: Vec<P>) -> NixideResult<Self> {
pub fn set_lookup_path<P: AsRef<str>>(self, paths: Vec<P>) -> NixideResult<Self> {
let paths_len = paths.len();
let paths_capacity = paths.capacity();

View file

@ -1,13 +1,13 @@
use std::cell::RefCell;
use std::ffi::c_char;
use std::ffi::{c_char, c_void};
use std::ptr::NonNull;
use crate::errors::ErrorContext;
use crate::expr::values::NixString;
use crate::stdext::CCharPtrExt;
use crate::sys;
use crate::util::wrappers::AsInnerPtr;
use crate::util::LazyArray;
use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap};
use crate::{EvalState, NixideResult, StorePath};
@ -17,6 +17,23 @@ pub struct RealisedString<'a> {
pub children: LazyArray<StorePath, Box<dyn Fn(usize) -> StorePath + 'a>>,
}
// impl<'a> Clone for RealisedString<'a> {
// 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,
// path: self.path.clone(),
// children: self.children,
// }
// }
// }
impl<'a> AsInnerPtr<sys::nix_realised_string> for RealisedString<'a> {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_realised_string {
@ -81,7 +98,7 @@ impl<'a> RealisedString<'a> {
path: Self::parse_path(inner.as_ptr(), state),
children: LazyArray::new(
size,
Box::new(|_| StorePath::fake_path(unsafe { state.store_ref() }).unwrap()),
Box::new(|_| StorePath::fake_path(state.store_ref()).unwrap()),
),
})
}
@ -98,7 +115,7 @@ impl<'a> RealisedString<'a> {
err
)
});
StorePath::parse(unsafe { state.store_ref() }, &path_str).unwrap_or_else(|err| {
StorePath::parse(state.store_ref(), &path_str).unwrap_or_else(|err| {
panic_issue_call_failed!(
"`sys::nix_realised_string_get_buffer_(start|size)` invalid store path ({})",
err

View file

@ -1,4 +1,4 @@
use std::sync::Arc;
use std::rc::Rc;
use serial_test::serial;
@ -8,7 +8,7 @@ use crate::Store;
#[test]
#[serial]
fn test_eval_state_builder() {
let store = Arc::new(Store::open(None).expect("Failed to open store"));
let store = Rc::new(Store::default().expect("Failed to open store"));
let _state = EvalStateBuilder::new(&store)
.expect("Failed to create builder")
.build()
@ -19,14 +19,14 @@ fn test_eval_state_builder() {
#[test]
#[serial]
fn test_simple_evaluation() {
let store = Arc::new(Store::open(None).expect("Failed to open store"));
let store = Rc::new(Store::default().expect("Failed to open store"));
let state = EvalStateBuilder::new(&store)
.expect("Failed to create builder")
.build()
.expect("Failed to build state");
let result = state
.eval_from_string("1 + 2", "<eval>")
.interpret("1 + 2", "<eval>")
.expect("Failed to evaluate expression");
assert!(matches!(result, Value::Int(_)));
@ -40,7 +40,7 @@ fn test_simple_evaluation() {
#[test]
#[serial]
fn test_value_types() {
let store = Arc::new(Store::open(None).expect("Failed to open store"));
let store = Store::default().expect("Failed to open store");
let state = EvalStateBuilder::new(&store)
.expect("Failed to create builder")
.build()
@ -48,7 +48,7 @@ fn test_value_types() {
// Test integer
let int_val = state
.eval_from_string("42", "<eval>")
.interpret("42", "<eval>")
.expect("Failed to evaluate int");
assert!(matches!(int_val, Value::Int(_)));
if let Value::Int(value) = int_val {
@ -59,7 +59,7 @@ fn test_value_types() {
// Test boolean
let bool_val = state
.eval_from_string("true", "<eval>")
.interpret("true", "<eval>")
.expect("Failed to evaluate bool");
assert!(matches!(bool_val, Value::Bool(_)));
if let Value::Bool(value) = bool_val {
@ -70,7 +70,7 @@ fn test_value_types() {
// Test string
let string_val = state
.eval_from_string("\"hello\"", "<eval>")
.interpret("\"hello\"", "<eval>")
.expect("Failed to evaluate string");
assert!(matches!(string_val, Value::String(_)));
if let Value::String(value) = string_val {
@ -83,7 +83,7 @@ fn test_value_types() {
#[test]
#[serial]
fn test_value_formatting() {
let store = Arc::new(Store::open(None).expect("Failed to open store"));
let store = Store::default().expect("Failed to open store");
let state = EvalStateBuilder::new(&store)
.expect("Failed to create builder")
.build()
@ -91,27 +91,27 @@ fn test_value_formatting() {
// Test integer formatting
let int_val = state
.eval_from_string("42", "<eval>")
.interpret("42", "<eval>")
.expect("Failed to evaluate int");
assert_eq!(format!("{int_val}"), "42");
assert_eq!(format!("{int_val:?}"), "Value::Int(NixInt(42))");
// Test boolean formatting
let true_val = state
.eval_from_string("true", "<eval>")
.interpret("true", "<eval>")
.expect("Failed to evaluate bool");
assert_eq!(format!("{true_val}"), "true");
assert_eq!(format!("{true_val:?}"), "Value::Bool(NixBool(true))");
let false_val = state
.eval_from_string("false", "<eval>")
.interpret("false", "<eval>")
.expect("Failed to evaluate bool");
assert_eq!(format!("{false_val}"), "false");
assert_eq!(format!("{false_val:?}"), "Value::Bool(NixBool(false))");
// Test string formatting
let str_val = state
.eval_from_string("\"hello world\"", "<eval>")
.interpret("\"hello world\"", "<eval>")
.expect("Failed to evaluate string");
assert_eq!(format!("{str_val}"), "hello world");
assert_eq!(
@ -121,7 +121,7 @@ fn test_value_formatting() {
// Test string with quotes
let quoted_str = state
.eval_from_string("\"say \\\"hello\\\"\"", "<eval>")
.interpret("\"say \\\"hello\\\"\"", "<eval>")
.expect("Failed to evaluate quoted string");
assert_eq!(format!("{quoted_str}"), "say \"hello\"");
assert_eq!(
@ -131,14 +131,14 @@ fn test_value_formatting() {
// Test null formatting
let null_val = state
.eval_from_string("null", "<eval>")
.interpret("null", "<eval>")
.expect("Failed to evaluate null");
assert_eq!(format!("{null_val}"), "null");
assert_eq!(format!("{null_val:?}"), "Value::Null(NixNull)");
// Test collection formatting
let attrs_val = state
.eval_from_string("{ a = 1; }", "<eval>")
.interpret("{ a = 1; }", "<eval>")
.expect("Failed to evaluate attrs");
assert_eq!(format!("{attrs_val}"), "{ <attrs> }");
assert_eq!(
@ -147,7 +147,7 @@ fn test_value_formatting() {
);
let list_val = state
.eval_from_string("[ 1 2 3 ]", "<eval>")
.interpret("[ 1 2 3 ]", "<eval>")
.expect("Failed to evaluate list");
assert_eq!(format!("{list_val}"), "[ <list> ]");
assert_eq!(format!("{list_val:?}"), "Value::List(NixList([ <list> ]))");

View file

@ -1,22 +1,38 @@
use std::cell::RefCell;
use std::ffi::c_void;
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::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::{EvalState, NixError};
pub struct NixAttrs {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
len: u32,
}
impl Clone for NixAttrs {
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,
state: self.state.clone(),
len: self.len,
}
}
}
impl Drop for NixAttrs {
fn drop(&mut self) {
let ctx = ErrorContext::new();
@ -61,13 +77,17 @@ impl NixValue for NixAttrs {
sys::ValueType_NIX_TYPE_ATTRS
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
let len = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_attrs_size(ctx.as_ptr(), inner.as_ptr())
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
Self { inner, state, len }
Self {
inner,
state: state.clone(),
len,
}
}
}
@ -88,7 +108,7 @@ impl NixAttrs {
sys::nix_get_attr_byidx(
ctx.as_ptr(),
self.as_ptr(),
self.state.borrow_mut().as_ptr(),
self.state.as_ptr(),
index,
name_ptr,
)
@ -99,7 +119,7 @@ impl NixAttrs {
.to_utf8_string()
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
let value = Value::from((inner, self.state.clone()));
let value = Value::from((inner, &self.state));
Some((name, value))
}
@ -115,7 +135,7 @@ impl NixAttrs {
sys::nix_get_attr_byidx_lazy(
ctx.as_ptr(),
self.as_ptr(),
self.state.borrow_mut().as_ptr(),
self.state.as_ptr(),
index,
name_ptr,
)
@ -126,7 +146,7 @@ impl NixAttrs {
.to_utf8_string()
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
let value = <NixThunk as NixValue>::from(inner, self.state.clone());
let value = <NixThunk as NixValue>::from(inner, &self.state);
Some((name, value))
}
@ -137,12 +157,7 @@ impl NixAttrs {
}
let name_ptr = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_attr_name_byidx(
ctx.as_ptr(),
self.as_ptr(),
self.state.borrow_mut().as_ptr(),
index,
)
sys::nix_get_attr_name_byidx(ctx.as_ptr(), self.as_ptr(), self.state.as_ptr(), index)
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
@ -161,7 +176,7 @@ impl NixAttrs {
sys::nix_get_attr_byname(
ctx.as_ptr(),
self.as_ptr(),
self.state.borrow_mut().as_ptr(),
self.state.as_ptr(),
name.as_ref()
.into_c_ptr()
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err)),
@ -169,7 +184,7 @@ impl NixAttrs {
});
match result {
Ok(inner) => Some(Value::from((inner, self.state.clone()))),
Ok(inner) => Some(Value::from((inner, &self.state))),
Err(NixideError::NixError {
err: NixError::KeyNotFound(_),
@ -187,7 +202,7 @@ impl NixAttrs {
sys::nix_get_attr_byname_lazy(
ctx.as_ptr(),
self.as_ptr(),
self.state.borrow_mut().as_ptr(),
self.state.as_ptr(),
name.as_ref()
.into_c_ptr()
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err)),
@ -195,7 +210,7 @@ impl NixAttrs {
});
match result {
Ok(inner) => Some(<NixThunk as NixValue>::from(inner, self.state.clone())),
Ok(inner) => Some(<NixThunk as NixValue>::from(inner, &self.state)),
Err(NixideError::NixError {
err: NixError::KeyNotFound(_),

View file

@ -1,21 +1,37 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::panic_issue_call_failed;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::{EvalState, sys};
pub struct NixBool {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
value: bool,
}
impl Clone for NixBool {
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,
state: self.state.clone(),
value: self.value,
}
}
}
impl Drop for NixBool {
fn drop(&mut self) {
let ctx = ErrorContext::new();
@ -60,7 +76,7 @@ impl NixValue for NixBool {
sys::ValueType_NIX_TYPE_BOOL
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_bool(ctx.as_ptr(), inner.as_ptr())
})
@ -70,7 +86,7 @@ impl NixValue for NixBool {
Self {
inner,
state,
state: state.clone(),
value,
}
}

View file

@ -1,9 +1,9 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue;
use crate::EvalState;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrappers::AsInnerPtr;
@ -11,10 +11,27 @@ use crate::util::{panic_issue_call_failed, wrap};
pub struct NixFloat {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
value: f64,
}
impl Clone for NixFloat {
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,
state: self.state.clone(),
value: self.value,
}
}
}
impl Drop for NixFloat {
fn drop(&mut self) {
let ctx = ErrorContext::new();
@ -59,7 +76,7 @@ impl NixValue for NixFloat {
sys::ValueType_NIX_TYPE_FLOAT
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_float(ctx.as_ptr(), inner.as_ptr())
})
@ -69,7 +86,7 @@ impl NixValue for NixFloat {
Self {
inner,
state,
state: state.clone(),
value,
}
}

View file

@ -1,19 +1,33 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::{NixValue, Value};
use crate::errors::ErrorContext;
use crate::stdext::SliceExt;
use crate::sys;
use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap};
use crate::{EvalState, sys};
pub struct NixFunction {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
value: i64,
state: EvalState,
}
impl Clone for NixFunction {
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,
state: self.state.clone(),
}
}
}
impl Drop for NixFunction {
@ -60,16 +74,10 @@ impl NixValue for NixFunction {
sys::ValueType_NIX_TYPE_FUNCTION
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_int(ctx.as_ptr(), inner.as_ptr())
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
Self {
inner,
state,
value,
state: state.clone(),
}
}
}
@ -80,14 +88,14 @@ impl NixFunction {
T: NixValue,
{
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_alloc_value(ctx.as_ptr(), self.state.borrow().as_ptr())
sys::nix_alloc_value(ctx.as_ptr(), self.state.as_ptr())
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_value_call(
ctx.as_ptr(),
self.state.borrow().as_ptr(),
self.state.as_ptr(),
self.as_ptr(),
arg.as_ptr(),
inner.as_ptr(),
@ -95,7 +103,7 @@ impl NixFunction {
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
Value::from((inner, self.state.clone()))
Value::from((inner, &self.state))
}
pub fn call_many<T>(&self, args: &[&T]) -> Value
@ -103,14 +111,14 @@ impl NixFunction {
T: NixValue,
{
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_alloc_value(ctx.as_ptr(), self.state.borrow().as_ptr())
sys::nix_alloc_value(ctx.as_ptr(), self.state.as_ptr())
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_value_call_multi(
ctx.as_ptr(),
self.state.borrow().as_ptr(),
self.state.as_ptr(),
self.as_ptr(),
args.len(),
args.into_c_array(),
@ -119,7 +127,7 @@ impl NixFunction {
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
Value::from((inner, self.state.clone()))
Value::from((inner, &self.state))
}
}

View file

@ -1,9 +1,9 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue;
use crate::EvalState;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrappers::AsInnerPtr;
@ -11,10 +11,27 @@ use crate::util::{panic_issue_call_failed, wrap};
pub struct NixInt {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
value: i64,
}
impl Clone for NixInt {
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,
state: self.state.clone(),
value: self.value,
}
}
}
impl Drop for NixInt {
fn drop(&mut self) {
let ctx = ErrorContext::new();
@ -59,7 +76,7 @@ impl NixValue for NixInt {
sys::ValueType_NIX_TYPE_INT
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_int(ctx.as_ptr(), inner.as_ptr())
})
@ -67,7 +84,7 @@ impl NixValue for NixInt {
Self {
inner,
state,
state: state.clone(),
value,
}
}

View file

@ -1,9 +1,9 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::{NixThunk, NixValue, Value};
use crate::EvalState;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrappers::AsInnerPtr;
@ -11,7 +11,23 @@ use crate::util::{panic_issue_call_failed, wrap};
pub struct NixList {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
}
impl Clone for NixList {
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,
state: self.state.clone(),
}
}
}
impl Drop for NixList {
@ -58,8 +74,11 @@ impl NixValue for NixList {
sys::ValueType_NIX_TYPE_LIST
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
Self { inner, state }
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
Self {
inner,
state: state.clone(),
}
}
}
@ -98,29 +117,19 @@ impl NixList {
pub fn get(&self, index: u32) -> Value {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_list_byidx(
ctx.as_ptr(),
self.as_ptr(),
self.state.borrow().as_ptr(),
index,
)
sys::nix_get_list_byidx(ctx.as_ptr(), self.as_ptr(), self.state.as_ptr(), index)
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
Value::from((inner, self.state.clone()))
Value::from((inner, &self.state))
}
pub fn get_lazy(&self, index: u32) -> NixThunk {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_list_byidx_lazy(
ctx.as_ptr(),
self.as_ptr(),
self.state.borrow().as_ptr(),
index,
)
sys::nix_get_list_byidx_lazy(ctx.as_ptr(), self.as_ptr(), self.state.as_ptr(), index)
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
<NixThunk as NixValue>::from(inner, self.state.clone())
<NixThunk as NixValue>::from(inner, &self.state)
}
}

View file

@ -23,10 +23,8 @@ pub use path::NixPath;
pub use string::NixString;
pub use thunk::NixThunk;
use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use crate::EvalState;
use crate::errors::ErrorContext;
@ -40,12 +38,12 @@ use crate::sys::{
use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap};
pub trait NixValue: Drop + Display + Debug + AsInnerPtr<sys::nix_value> {
pub trait NixValue: Clone + Drop + Display + Debug + AsInnerPtr<sys::nix_value> {
/// TODO
fn type_id(&self) -> sys::ValueType;
/// TODO
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self;
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self;
}
/// A Nix value
@ -111,22 +109,12 @@ pub enum Value {
// Failed(NixFailed),
}
impl
From<(
NonNull<sys::nix_value>,
Rc<RefCell<NonNull<sys::EvalState>>>,
)> for Value
{
fn from(
value: (
NonNull<sys::nix_value>,
Rc<RefCell<NonNull<sys::EvalState>>>,
),
) -> Self {
impl From<(NonNull<sys::nix_value>, &EvalState)> for Value {
fn from(value: (NonNull<sys::nix_value>, &EvalState)) -> Self {
let (inner, state) = value;
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_value_force(ctx.as_ptr(), state.borrow().as_ptr(), inner.as_ptr())
sys::nix_value_force(ctx.as_ptr(), state.as_ptr(), inner.as_ptr())
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
@ -197,13 +185,3 @@ impl Debug for Value {
}
}
}
// macro_rules! is_type {
// ($expr:expr, $tt:tt) => {{
// match $expr {
// $tt => true,
// _ => false,
// }
// }};
// }
// pub(self) use is_type;

View file

@ -1,16 +1,33 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue;
use crate::EvalState;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
pub struct NixNull {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
}
impl Clone for NixNull {
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,
state: self.state.clone(),
}
}
}
impl Drop for NixNull {
@ -57,7 +74,10 @@ impl NixValue for NixNull {
sys::ValueType_NIX_TYPE_NULL
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
Self { inner, state }
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
Self {
inner,
state: state.clone(),
}
}
}

View file

@ -1,23 +1,39 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::path::PathBuf;
use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue;
use crate::errors::ErrorContext;
use crate::stdext::CCharPtrExt;
use crate::sys;
use crate::util::panic_issue_call_failed;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::{EvalState, sys};
pub struct NixPath {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
value: PathBuf,
}
impl Clone for NixPath {
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,
state: self.state.clone(),
value: self.value.clone(),
}
}
}
impl Drop for NixPath {
fn drop(&mut self) {
let ctx = ErrorContext::new();
@ -62,7 +78,7 @@ impl NixValue for NixPath {
sys::ValueType_NIX_TYPE_PATH
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_path_string(ctx.as_ptr(), inner.as_ptr())
})
@ -72,7 +88,7 @@ impl NixValue for NixPath {
Self {
inner,
state,
state: state.clone(),
value,
}
}

View file

@ -1,22 +1,37 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::panic_issue_call_failed;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::{EvalState, sys};
pub struct NixString {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
value: String,
}
impl Clone for NixString {
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,
state: self.state.clone(),
value: self.value.clone(),
}
}
}
impl Drop for NixString {
fn drop(&mut self) {
let ctx = ErrorContext::new();
@ -61,7 +76,7 @@ impl NixValue for NixString {
sys::ValueType_NIX_TYPE_STRING
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
let value = wrap::nix_string_callback!(
|callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe {
sys::nix_get_string(
@ -76,7 +91,7 @@ impl NixValue for NixString {
Self {
inner,
state,
state: state.clone(),
value,
}
}

View file

@ -1,9 +1,9 @@
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull;
use std::rc::Rc;
use super::{NixValue, Value};
use crate::EvalState;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrappers::AsInnerPtr;
@ -11,7 +11,23 @@ use crate::util::{panic_issue_call_failed, wrap};
pub struct NixThunk {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<NonNull<sys::EvalState>>>,
state: EvalState,
}
impl Clone for NixThunk {
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,
state: self.state.clone(),
}
}
}
impl Drop for NixThunk {
@ -58,26 +74,21 @@ impl NixValue for NixThunk {
sys::ValueType_NIX_TYPE_THUNK
}
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<NonNull<sys::EvalState>>>) -> Self {
Self { inner, state }
fn from(inner: NonNull<sys::nix_value>, state: &EvalState) -> Self {
Self {
inner,
state: state.clone(),
}
}
}
impl NixThunk {
// fn to_inner(self) -> NonNull<sys::nix_value> {
// self.inner
// }
pub fn eval(self) -> Value {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_value_force(
ctx.as_ptr(),
self.state.borrow().as_ptr(),
self.inner.as_ptr(),
)
sys::nix_value_force(ctx.as_ptr(), self.state.as_ptr(), self.inner.as_ptr())
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
Value::from((self.inner, self.state.to_owned()))
Value::from((self.inner, &self.state))
}
}

View file

@ -1,14 +0,0 @@
use super::FlakeSettings;
use crate::{EvalStateBuilder, NixideError};
pub trait EvalStateBuilderExt {
/// Configures the eval state to provide flakes features such as `builtins.getFlake`.
fn flakes(self, settings: &FlakeSettings) -> Result<EvalStateBuilder, NixideError>;
}
impl EvalStateBuilderExt for EvalStateBuilder {
/// Configures the eval state to provide flakes features such as `builtins.getFlake`.
fn flakes(mut self, settings: &FlakeSettings) -> Result<EvalStateBuilder, NixideError> {
settings.add_to_eval_state_builder(&mut self).map(|_| self)
}
}

View file

@ -1,28 +1,39 @@
use std::ffi::c_void;
use std::ptr::NonNull;
use crate::errors::{new_nixide_error, ErrorContext};
use crate::NixideResult;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::NixideError;
pub(super) struct FetchersSettings {
pub(super) ptr: NonNull<sys::nix_fetchers_settings>,
pub struct FetchersSettings {
inner: NonNull<sys::nix_fetchers_settings>,
}
impl FetchersSettings {
pub fn new() -> Result<Self, NixideError> {
let ctx = ErrorContext::new();
let ptr = unsafe { sys::nix_fetchers_settings_new(ctx.as_ptr()) };
Ok(FetchersSettings {
ptr: NonNull::new(ptr).ok_or(new_nixide_error!(NullPtr))?,
})
}
pub fn new() -> NixideResult<Self> {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_fetchers_settings_new(ctx.as_ptr())
})?;
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_fetchers_settings {
self.ptr.as_ptr()
Ok(Self { inner })
}
}
// 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 {
@ -31,6 +42,23 @@ impl Drop for FetchersSettings {
}
}
impl AsInnerPtr<sys::nix_fetchers_settings> for FetchersSettings {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_fetchers_settings {
self.inner.as_ptr()
}
#[inline]
unsafe fn as_ref(&self) -> &sys::nix_fetchers_settings {
unsafe { self.inner.as_ref() }
}
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::nix_fetchers_settings {
unsafe { self.inner.as_mut() }
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,22 +1,26 @@
use std::ffi::CString;
use std::ffi::c_void;
use std::ptr::NonNull;
use super::{FlakeReference, FlakeSettings};
use crate::errors::new_nixide_error;
use crate::NixideResult;
use crate::errors::ErrorContext;
use crate::stdext::AsCPtr as _;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::NixideError;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum FlakeLockMode {
/// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file.
///
WriteAsNeeded,
/// Like [FlakeLockMode::WriteAsNeeded], but does not write to the lock file.
///
Virtual,
/// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned.
///
Check,
}
@ -24,6 +28,20 @@ pub enum FlakeLockMode {
pub struct FlakeLockFlags {
pub(crate) inner: NonNull<sys::nix_flake_lock_flags>,
}
// impl Clone for FlakeLockFlags {
// 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 FlakeLockFlags {
fn drop(&mut self) {
unsafe {
@ -31,69 +49,49 @@ impl Drop for FlakeLockFlags {
}
}
}
impl FlakeLockFlags {
// XXX: TODO: what is the default FlakeLockMode?
pub fn new(settings: &FlakeSettings) -> Result<Self, NixideError> {
let ctx = ErrorContext::new();
NonNull::new(unsafe { sys::nix_flake_lock_flags_new(ctx.as_ptr(), settings.as_ptr()) })
.ok_or(new_nixide_error!(NullPtr))
.map(|inner| FlakeLockFlags { inner })
}
pub(crate) fn as_ptr(&self) -> *mut sys::nix_flake_lock_flags {
impl AsInnerPtr<sys::nix_flake_lock_flags> for FlakeLockFlags {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_flake_lock_flags {
self.inner.as_ptr()
}
pub fn set_lock_mode(&mut self, mode: &FlakeLockMode) -> Result<(), NixideError> {
let ctx = ErrorContext::new();
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())
},
};
match ctx.peak() {
Some(err) => Err(err),
None => Ok(()),
}
#[inline]
unsafe fn as_ref(&self) -> &sys::nix_flake_lock_flags {
unsafe { self.inner.as_ref() }
}
/// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file.
// pub fn set_mode_write_as_needed(&mut self) -> Result<(), NixideError> {
// ErrorContext::new().and_then(|ctx| {
// NixideError::from(
// unsafe {
// sys::nix_flake_lock_flags_set_mode_write_as_needed(ctx.as_ptr(), self.as_ptr())
// },
// "nix_flake_lock_flags_set_mode_write_as_needed",
// )
// })
// }
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::nix_flake_lock_flags {
unsafe { self.inner.as_mut() }
}
}
/// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned.
// pub fn set_mode_check(&mut self) -> Result<(), NixideError> {
// ErrorContext::new().and_then(|ctx| {
// NixideError::from(
// unsafe { sys::nix_flake_lock_flags_set_mode_check(ctx.as_ptr(), self.as_ptr()) },
// "nix_flake_lock_flags_set_mode_check",
// )
// })
// }
impl FlakeLockFlags {
// XXX: TODO: what is the default FlakeLockMode?
pub fn new(settings: &FlakeSettings) -> NixideResult<Self> {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_flake_lock_flags_new(ctx.as_ptr(), settings.as_ptr())
})?;
/// Like `set_mode_write_as_needed`, but does not write to the lock file.
// pub fn set_mode_virtual(&mut self) -> Result<(), NixideError> {
// ErrorContext::new().and_then(|ctx| {
// NixideError::from(
// unsafe { sys::nix_flake_lock_flags_set_mode_virtual(ctx.as_ptr(), self.as_ptr()) },
// "nix_flake_lock_flags_set_mode_virtual",
// )
// })
// }
Ok(FlakeLockFlags { inner })
}
pub fn set_mode(&mut 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())
},
};
})
}
/// Adds an input override to the lock file that will be produced.
/// The [LockedFlake::lock] operation will not write to the lock file.
@ -106,26 +104,17 @@ impl FlakeLockFlags {
/// # Arguments
///
/// * `path` - The input name/path to override (must not be empty)
/// * `flake_ref` - The flake reference to use as the override
pub fn override_input(
&mut self,
path: &str,
flakeref: &FlakeReference,
) -> Result<(), NixideError> {
let input_path = CString::new(path).or_else(|_| Err(new_nixide_error!(StringNulByte)));
/// * `flakeref` - The flake reference to use as the override
pub fn override_input(&mut self, path: &str, flakeref: &FlakeReference) -> NixideResult<()> {
let input_path = path.as_c_ptr()?;
let ctx = ErrorContext::new();
unsafe {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_flake_lock_flags_add_input_override(
ctx.as_ptr(),
self.as_ptr(),
input_path.as_ptr(),
input_path,
flakeref.as_ptr(),
)
};
match ctx.peak() {
Some(err) => Err(err),
None => Ok(()),
}
);
})
}
}

View file

@ -1,58 +1,99 @@
use std::ffi::c_void;
use std::os::raw::c_char;
use std::ptr::{null_mut, NonNull};
use std::ptr::{NonNull, null_mut};
use super::{FetchersSettings, FlakeReferenceParseFlags, FlakeSettings};
use crate::errors::new_nixide_error;
use crate::sys;
use crate::util::bindings::wrap_nix_string_callback;
use crate::util::wrappers::AsInnerPtr;
use crate::NixideError;
use crate::errors::{ErrorContext, new_nixide_error};
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
// XXX: TODO: rename FlakeReference -> FlakeRef
pub struct FlakeReference {
pub(crate) ptr: NonNull<sys::nix_flake_reference>,
inner: NonNull<sys::nix_flake_reference>,
fragment: String,
}
// 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 FlakeReference {
fn drop(&mut self) {
unsafe {
sys::nix_flake_reference_free(self.ptr.as_ptr());
sys::nix_flake_reference_free(self.as_ptr());
}
}
}
impl AsInnerPtr<sys::nix_flake_reference> for FlakeReference {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_flake_reference {
self.inner.as_ptr()
}
#[inline]
unsafe fn as_ref(&self) -> &sys::nix_flake_reference {
unsafe { self.inner.as_ref() }
}
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::nix_flake_reference {
unsafe { self.inner.as_mut() }
}
}
impl FlakeReference {
pub fn as_ptr(&self) -> *mut sys::nix_flake_reference {
self.ptr.as_ptr()
}
/// 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_with_fragment(
pub fn parse(
fetch_settings: &FetchersSettings,
flake_settings: &FlakeSettings,
flags: &FlakeReferenceParseFlags,
reference: &str,
) -> Result<(FlakeReference, String), NixideError> {
) -> Result<FlakeReference, NixideError> {
let mut ptr: *mut sys::nix_flake_reference = null_mut();
let result = wrap_nix_string_callback(|ctx, callback, user_data| unsafe {
sys::nix_flake_reference_and_fragment_from_string(
ctx.as_ptr(),
fetch_settings.as_ptr(),
flake_settings.as_ptr(),
flags.as_ptr(),
reference.as_ptr() as *const c_char,
reference.len(),
&mut ptr,
Some(callback),
user_data,
)
});
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(),
flags.as_ptr(),
reference.as_ptr() as *const c_char,
reference.len(),
&mut ptr,
Some(callback),
userdata as *mut c_void,
)
}
)?;
match NonNull::new(ptr) {
Some(ptr) => result.map(|s| (FlakeReference { ptr }, s)),
Some(inner) => Ok(FlakeReference { inner, fragment }),
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
}
}

View file

@ -1,58 +1,77 @@
use std::ffi::c_void;
use std::os::raw::c_char;
use std::ptr::NonNull;
use super::FlakeSettings;
use crate::errors::{new_nixide_error, ErrorContext};
use crate::NixideResult;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::NixideError;
/// Parameters for parsing a flake reference.
#[derive(Debug)]
pub struct FlakeReferenceParseFlags {
pub(crate) ptr: NonNull<sys::nix_flake_reference_parse_flags>,
inner: NonNull<sys::nix_flake_reference_parse_flags>,
}
// 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 FlakeReferenceParseFlags {
fn drop(&mut self) {
unsafe {
sys::nix_flake_reference_parse_flags_free(self.ptr.as_ptr());
sys::nix_flake_reference_parse_flags_free(self.inner.as_ptr());
}
}
}
impl AsInnerPtr<sys::nix_flake_reference_parse_flags> for FlakeReferenceParseFlags {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_flake_reference_parse_flags {
self.inner.as_ptr()
}
#[inline]
unsafe fn as_ref(&self) -> &sys::nix_flake_reference_parse_flags {
unsafe { self.inner.as_ref() }
}
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::nix_flake_reference_parse_flags {
unsafe { self.inner.as_mut() }
}
}
impl FlakeReferenceParseFlags {
pub fn new(settings: &FlakeSettings) -> Result<Self, NixideError> {
let ctx = ErrorContext::new();
let ptr =
unsafe { sys::nix_flake_reference_parse_flags_new(ctx.as_ptr(), settings.as_ptr()) };
match ctx.peak() {
Some(err) => Err(err),
None => NonNull::new(ptr).map_or(Err(new_nixide_error!(NullPtr)), |ptr| {
Ok(FlakeReferenceParseFlags { ptr })
}),
}
pub fn new(settings: &FlakeSettings) -> NixideResult<Self> {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_flake_reference_parse_flags_new(ctx.as_ptr(), settings.as_ptr())
})?;
Ok(Self { inner })
}
/// Sets the [base directory](https://nix.dev/manual/nix/latest/glossary#gloss-base-directory)
/// for resolving local flake references.
pub fn set_base_directory(&mut self, base_directory: &str) -> Result<(), NixideError> {
let ctx = ErrorContext::new();
unsafe {
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(),
base_directory.as_ptr() as *const c_char,
base_directory.len(),
)
};
match ctx.peak() {
Some(err) => Err(err),
None => Ok(()),
}
}
pub fn as_ptr(&self) -> *mut sys::nix_flake_reference_parse_flags {
self.ptr.as_ptr()
);
})
}
}

View file

@ -1,52 +1,15 @@
use std::ffi::c_void;
use std::ptr::NonNull;
use crate::errors::{ErrorContext, new_nixide_error};
use crate::NixideResult;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::{EvalStateBuilder, NixideError};
/// Store settings for the flakes feature.
pub struct FlakeSettings {
pub(crate) inner: NonNull<sys::nix_flake_settings>,
}
impl AsInnerPtr<sys::nix_flake_settings> for FlakeSettings {
unsafe fn as_ptr(&self) -> *mut sys::nix_flake_settings {
self.inner.as_ptr()
}
}
impl FlakeSettings {
pub fn new() -> Result<Self, NixideError> {
let ctx = ErrorContext::new();
let opt = NonNull::new(unsafe { sys::nix_flake_settings_new(ctx.as_ptr()) });
match ctx.peak() {
Some(err) => Err(err),
None => match opt {
Some(inner) => Ok(FlakeSettings { inner }),
None => Err(new_nixide_error!(NullPtr)),
},
}
}
pub(super) fn add_to_eval_state_builder(
&self,
builder: &mut EvalStateBuilder,
) -> Result<(), NixideError> {
let ctx = ErrorContext::new();
unsafe {
sys::nix_flake_settings_add_to_eval_state_builder(
ctx.as_ptr(),
self.as_ptr(),
builder.as_ptr(),
)
};
match ctx.peak() {
Some(err) => Err(err),
None => Ok(()),
}
}
inner: NonNull<sys::nix_flake_settings>,
}
impl Drop for FlakeSettings {
@ -56,3 +19,43 @@ impl Drop for FlakeSettings {
}
}
}
// impl Clone for FlakeSettings {
// 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<sys::nix_flake_settings> for FlakeSettings {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_flake_settings {
self.inner.as_ptr()
}
#[inline]
unsafe fn as_ref(&self) -> &sys::nix_flake_settings {
unsafe { self.inner.as_ref() }
}
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::nix_flake_settings {
unsafe { self.inner.as_mut() }
}
}
impl FlakeSettings {
pub fn new() -> NixideResult<Self> {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_flake_settings_new(ctx.as_ptr())
})?;
Ok(Self { inner })
}
}

View file

@ -1,145 +1,184 @@
// XXX: TODO: find a way to read directly from FlakeSettings and FetchersSettings (the C++ classes)
use std::ffi::c_void;
use std::ptr::NonNull;
use super::{FetchersSettings, FlakeLockFlags, FlakeReference, FlakeSettings};
use crate::errors::{new_nixide_error, ErrorContext};
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::{EvalState, NixideError, Value};
use crate::{EvalState, NixideResult, Value};
pub struct LockedFlake {
pub(crate) ptr: NonNull<sys::nix_locked_flake>,
inner: NonNull<sys::nix_locked_flake>,
flakeref: FlakeReference,
state: EvalState,
flags: FlakeLockFlags,
fetch_settings: FetchersSettings,
flake_settings: FlakeSettings,
}
// impl Clone for LockedFlake {
// 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(),
// flakeref: self.flakeref.clone(),
// state: self.state.clone(),
// flags: self.flags.clone(),
// fetch_settings: self.fetch_settings.clone(),
// flake_settings: self.flake_settings.clone(),
// }
// }
// }
impl Drop for LockedFlake {
fn drop(&mut self) {
unsafe {
sys::nix_locked_flake_free(self.ptr.as_ptr());
sys::nix_locked_flake_free(self.as_ptr());
}
}
}
impl AsInnerPtr<sys::nix_locked_flake> for LockedFlake {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_locked_flake {
self.inner.as_ptr()
}
#[inline]
unsafe fn as_ref(&self) -> &sys::nix_locked_flake {
unsafe { self.inner.as_ref() }
}
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::nix_locked_flake {
unsafe { self.inner.as_mut() }
}
}
impl LockedFlake {
pub fn lock(
fetch_settings: &FetchersSettings,
flake_settings: &FlakeSettings,
eval_state: &EvalState,
state: &EvalState,
flags: &FlakeLockFlags,
flake_ref: &FlakeReference,
) -> Result<LockedFlake, NixideError> {
let ctx = ErrorContext::new();
let ptr = NonNull::new(unsafe {
flakeref: &FlakeReference,
) -> NixideResult<LockedFlake> {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_flake_lock(
ctx.as_ptr(),
fetch_settings.as_ptr(),
flake_settings.as_ptr(),
eval_state.as_ptr(),
state.as_ptr(),
flags.as_ptr(),
flake_ref.as_ptr(),
flakeref.as_ptr(),
)
});
})?;
match ptr {
Some(ptr) => Ok(LockedFlake { ptr }),
None => Err(new_nixide_error!(NullPtr)),
}
Ok(Self {
inner,
flakeref: flakeref.clone(),
state: state.clone(),
flags: flags.clone(),
fetch_settings: fetch_settings.clone(),
flake_settings: flake_settings.clone(),
})
}
/// Returns the outputs of the flake - the result of calling the `outputs` attribute.
pub fn outputs(
&self,
flake_settings: &FlakeSettings,
eval_state: &mut EvalState,
) -> Result<Value, NixideError> {
let ctx = ErrorContext::new();
let r = unsafe {
pub fn outputs(&self) -> NixideResult<Value> {
let value = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_locked_flake_get_output_attrs(
ctx.as_ptr(),
flake_settings.as_ptr(),
eval_state.as_ptr(),
self.ptr.as_ptr(),
self.flake_settings.as_ptr(),
self.state.as_ptr(),
self.inner.as_ptr(),
)
};
Ok(nix_bindings_expr::value::__private::raw_value_new(r))
})?;
Ok(Value::from((value, &self.state)))
}
}
#[cfg(test)]
mod tests {
// use nix_bindings_expr::eval_state::{gc_register_my_thread, EvalStateBuilder};
use crate::flake::{FlakeLockMode, FlakeReferenceParseFlags};
use crate::{EvalStateBuilder, Store};
use super::*;
use std::fs;
use std::sync::Once;
use super::{FetchersSettings, FlakeLockFlags, FlakeReference, FlakeSettings, LockedFlake};
use crate::flake::{FlakeLockMode, FlakeReferenceParseFlags};
use crate::{EvalStateBuilder, Store, Value, set_global_setting};
static INIT: Once = Once::new();
fn init() {
// Only set experimental-features once to minimize the window where
// concurrent Nix operations might read the setting while it's being modified
INIT.call_once(|| {
nix_bindings_expr::eval_state::init().unwrap();
nix_bindings_util::settings::set("experimental-features", "flakes").unwrap();
INIT.call_once(|| unsafe {
set_global_setting("experimental-features", "flakes").unwrap();
});
}
#[test]
fn flake_settings_getflake_exists() {
init();
let gc_registration = gc_register_my_thread();
let store = Store::open(None, []).unwrap();
let mut eval_state = EvalStateBuilder::new(store)
let store = Store::default().expect("Failed to open store connection");
let state = EvalStateBuilder::new(&store)
.unwrap()
.flakes(&FlakeSettings::new().unwrap())
.flakes()
.unwrap()
.build()
.unwrap();
let v = eval_state
.eval_from_string("builtins?getFlake", "<test>")
.unwrap();
let value = state.interpret("builtins?getFlake", "<test>").unwrap();
let b = eval_state.require_bool(&v).unwrap();
assert!(b);
drop(gc_registration);
assert!(matches!(value, Value::Bool(_)));
if let Value::Bool(v) = value {
assert!(v.as_bool());
}
}
#[test]
fn flake_lock_load_flake() {
init();
let gc_registration = gc_register_my_thread();
let store = Store::open(None, []).unwrap();
let fetchers_settings = FetchersSettings::new().unwrap();
let flake_settings = FlakeSettings::new().unwrap();
let mut eval_state = EvalStateBuilder::new(store)
.unwrap()
.flakes(&flake_settings)
.unwrap()
.build()
.unwrap();
let tmp_dir = tempfile::tempdir().unwrap();
// Create flake.nix
let tmp_dir = tempfile::tempdir().unwrap();
let flake_nix = tmp_dir.path().join("flake.nix");
std::fs::write(
fs::write(
&flake_nix,
r#"
{
outputs = { ... }: {
hello = "potato";
hello = "world";
};
}
"#,
)
.unwrap();
let store = Store::default().unwrap();
let flake_settings = FlakeSettings::new().unwrap();
let mut eval_state = EvalStateBuilder::new(&store)
.unwrap()
.set_flake_settings(&flake_settings)
.unwrap()
.build()
.unwrap();
let fetchers_settings = FetchersSettings::new().unwrap();
let flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap();
let (flake_ref, fragment) = FlakeReference::parse_with_fragment(
let flakeref = FlakeReference::parse(
&fetchers_settings,
&flake_settings,
&FlakeReferenceParseFlags::new(&flake_settings).unwrap(),
@ -147,41 +186,40 @@ mod tests {
)
.unwrap();
assert_eq!(fragment, "subthing");
assert_eq!(flakeref.fragment(), "subthing");
let locked_flake = LockedFlake::lock(
&fetchers_settings,
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref,
&flakeref,
)
.unwrap();
let outputs = locked_flake
.outputs(&flake_settings, &mut eval_state)
.unwrap();
let outputs = locked_flake.outputs().unwrap();
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
let hello = eval_state.require_string(&hello).unwrap();
assert!(matches!(outputs, Value::Attrs(_)));
if let Value::Attrs(outputs) = outputs {
let value = outputs.get("hello").unwrap();
assert_eq!(hello, "potato");
drop(fetchers_settings);
drop(tmp_dir);
drop(gc_registration);
assert!(matches!(value, Value::String(_)));
if let Value::String(value) = value {
assert_eq!(value.as_string(), "world");
}
}
}
#[test]
fn flake_lock_load_flake_with_flags() {
init();
let gc_registration = gc_register_my_thread();
let store = Store::open(None, []).unwrap();
let store = Store::default().unwrap();
let fetchers_settings = FetchersSettings::new().unwrap();
let flake_settings = FlakeSettings::new().unwrap();
let mut eval_state = EvalStateBuilder::new(store)
let mut eval_state = EvalStateBuilder::new(&store)
.unwrap()
.flakes(&flake_settings)
.set_flake_settings(&flake_settings)
.unwrap()
.build()
.unwrap();
@ -222,7 +260,7 @@ mod tests {
r#"
{
outputs = { ... }: {
hello = "BOB";
hello = "ALICE";
};
}
"#,
@ -251,7 +289,7 @@ mod tests {
.set_base_directory(tmp_dir.path().to_str().unwrap())
.unwrap();
let (flake_ref_a, fragment) = FlakeReference::parse_with_fragment(
let flakeref_a = FlakeReference::parse(
&fetchers_settings,
&flake_settings,
&flake_reference_parse_flags,
@ -259,20 +297,17 @@ mod tests {
)
.unwrap();
assert_eq!(fragment, "");
assert_eq!(flakeref_a.fragment(), "");
// Step 1: Do not update (check), fails
flake_lock_flags
.set_lock_mode(&FlakeLockMode::Check)
.unwrap();
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(
&fetchers_settings,
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref_a,
&flakeref_a,
);
// Has not been locked and would need to write a lock file.
assert!(locked_flake.is_err());
@ -282,54 +317,50 @@ mod tests {
};
// Step 2: Update but do not write, succeeds
flake_lock_flags
.set_lock_mode(&FlakeLockMode::Virtual)
.unwrap();
flake_lock_flags.set_mode(&FlakeLockMode::Virtual).unwrap();
let locked_flake = LockedFlake::lock(
&fetchers_settings,
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref_a,
&flakeref_a,
)
.unwrap();
let outputs = locked_flake
.outputs(&flake_settings, &mut eval_state)
.unwrap();
let outputs = locked_flake.outputs().unwrap();
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
let hello = eval_state.require_string(&hello).unwrap();
assert!(matches!(outputs, Value::Attrs(_)));
if let Value::Attrs(outputs) = outputs {
let value = outputs.get("hello").unwrap();
assert_eq!(hello, "BOB");
assert!(matches!(value, Value::String(_)));
if let Value::String(value) = value {
assert_eq!(value.as_string(), "ALICE");
}
}
// Step 3: The lock was not written, so Step 1 would fail again
flake_lock_flags
.set_lock_mode(&FlakeLockMode::Check)
.unwrap();
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(
&fetchers_settings,
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref_a,
&flakeref_a,
);
// Has not been locked and would need to write a lock file.
assert!(locked_flake.is_err());
match locked_flake {
Ok(_) => panic!("Expected error, but got Ok"),
Err(e) => {
assert_eq!(e.to_string(), saved_err.to_string());
}
},
};
// Step 4: Update and write, succeeds
flake_lock_flags
.set_lock_mode(&FlakeLockMode::WriteAsNeeded)
.set_mode(&FlakeLockMode::WriteAsNeeded)
.unwrap();
let locked_flake = LockedFlake::lock(
@ -337,100 +368,110 @@ mod tests {
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref_a,
&flakeref_a,
)
.unwrap();
let outputs = locked_flake
.outputs(&flake_settings, &mut eval_state)
.unwrap();
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
let hello = eval_state.require_string(&hello).unwrap();
assert_eq!(hello, "BOB");
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");
}
}
// Step 5: Lock was written, so Step 1 succeeds
flake_lock_flags
.set_lock_mode(&FlakeLockMode::Check)
.unwrap();
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(
&fetchers_settings,
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref_a,
&flakeref_a,
)
.unwrap();
let outputs = locked_flake
.outputs(&flake_settings, &mut eval_state)
.unwrap();
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
let hello = eval_state.require_string(&hello).unwrap();
assert_eq!(hello, "BOB");
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");
}
}
// Step 6: Lock with override, do not write
// This shouldn't matter; write_as_needed will be overridden
flake_lock_flags
.set_lock_mode(&FlakeLockMode::WriteAsNeeded)
.set_mode(&FlakeLockMode::WriteAsNeeded)
.unwrap();
let (flake_ref_c, fragment) = FlakeReference::parse_with_fragment(
let flakeref_c = FlakeReference::parse(
&fetchers_settings,
&flake_settings,
&flake_reference_parse_flags,
&format!("path:{}", &flake_dir_c_str),
)
.unwrap();
assert_eq!(fragment, "");
assert_eq!(flakeref_c.fragment(), "");
flake_lock_flags.override_input("b", &flake_ref_c).unwrap();
flake_lock_flags.override_input("b", &flakeref_c).unwrap();
let locked_flake = LockedFlake::lock(
&fetchers_settings,
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref_a,
&flakeref_a,
)
.unwrap();
let outputs = locked_flake
.outputs(&flake_settings, &mut eval_state)
.unwrap();
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
let hello = eval_state.require_string(&hello).unwrap();
assert_eq!(hello, "Claire");
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(), "Claire");
}
}
// Can't delete overrides, so trash it
let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap();
// Step 7: Override was not written; lock still points to b
flake_lock_flags
.set_lock_mode(&FlakeLockMode::Check)
.unwrap();
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(
&fetchers_settings,
&flake_settings,
&eval_state,
&flake_lock_flags,
&flake_ref_a,
&flakeref_a,
)
.unwrap();
let outputs = locked_flake
.outputs(&flake_settings, &mut eval_state)
.unwrap();
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
let hello = eval_state.require_string(&hello).unwrap();
assert_eq!(hello, "BOB");
let outputs = locked_flake.outputs().unwrap();
drop(fetchers_settings);
drop(tmp_dir);
drop(gc_registration);
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");
}
}
}
}

View file

@ -5,9 +5,9 @@ mod flake_reference_parse_flags;
mod flake_settings;
mod locked_flake;
pub(self) use fetchers_settings::FetchersSettings;
pub(self) use flake_lock_flags::{FlakeLockFlags, FlakeLockMode};
pub(self) use flake_reference::FlakeReference;
pub(self) use flake_reference_parse_flags::FlakeReferenceParseFlags;
use fetchers_settings::FetchersSettings;
use flake_lock_flags::{FlakeLockFlags, FlakeLockMode};
use flake_reference::FlakeReference;
use flake_reference_parse_flags::FlakeReferenceParseFlags;
pub use flake_settings::FlakeSettings;
pub use locked_flake::LockedFlake;

View file

@ -7,6 +7,14 @@ use super::util::wrappers::AsInnerPtr as _;
pub(crate) static mut LIBNIX_INIT_STATUS: Option<NixideResult<()>> = None;
/// Initialises the Nix `libutil` library's global state.
/// This function **should not be run directly!** `#[ctor]` ensures it runs at init-time.
///
/// # Note
///
/// `sys::nix_libexpr_init` internally runs `sys::nix_libutil_init` and `sys::nix_libstore_init`.
/// Hence this method isn't registered via `#[ctor]` if the `exprs` feature is enabled.
///
/// # Warning
///
/// > Rust's philosophy is that nothing happens before or after main and [ctor](https://github.com/mmastrac/rust-ctor)
@ -16,7 +24,7 @@ pub(crate) static mut LIBNIX_INIT_STATUS: Option<NixideResult<()>> = None;
/// > - Excerpt from the [github:mmastrac/rust-ctor README.md](https://github.com/mmastrac/rust-ctor?tab=readme-ov-file#warnings)
#[ctor]
#[cfg(not(feature = "exprs"))]
fn init_libutil() {
pub(crate) fn init_libutil() {
unsafe {
if !matches!(LIBNIX_INIT_STATUS, Some(Err(_))) {
LIBNIX_INIT_STATUS = Some(wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
@ -26,6 +34,14 @@ fn init_libutil() {
}
}
/// Initialises the Nix `libstore` library's global state.
/// This function **should not be run directly!** `#[ctor]` ensures it runs at init-time.
///
/// # Note
///
/// `sys::nix_libexpr_init` internally runs `sys::nix_libutil_init` and `sys::nix_libstore_init`.
/// Hence this method isn't registered via `#[ctor]` if the `exprs` feature is enabled.
///
/// # Warning
///
/// > Rust's philosophy is that nothing happens before or after main and [ctor](https://github.com/mmastrac/rust-ctor)
@ -35,7 +51,7 @@ fn init_libutil() {
/// > - Excerpt from the [github:mmastrac/rust-ctor README.md](https://github.com/mmastrac/rust-ctor?tab=readme-ov-file#warnings)
#[ctor]
#[cfg(all(feature = "store", not(feature = "exprs")))]
fn init_libstore() {
pub(crate) fn init_libstore() {
// XXX: TODO: how do I support `sys::nix_libstore_init_no_load_config(context)`?
unsafe {
if !matches!(LIBNIX_INIT_STATUS, Some(Err(_))) {
@ -46,7 +62,12 @@ fn init_libstore() {
}
}
/// `sys::nix_libexpr_init` internally runs
/// Initialises the Nix `libexpr` library's global state.
/// This function **should not be run directly!** `#[ctor]` ensures it runs at init-time.
///
/// # Note
///
/// `sys::nix_libexpr_init` internally runs `sys::nix_libutil_init` and `sys::nix_libstore_init`.
///
/// # Warning
///
@ -57,7 +78,7 @@ fn init_libstore() {
/// > - Excerpt from the [github:mmastrac/rust-ctor README.md](https://github.com/mmastrac/rust-ctor?tab=readme-ov-file#warnings)
#[ctor]
#[cfg(feature = "exprs")]
fn init_libexpr() {
pub(crate) fn init_libexpr() {
unsafe {
if !matches!(LIBNIX_INIT_STATUS, Some(Err(_))) {
LIBNIX_INIT_STATUS = Some(wrap::nix_fn!(|ctx: &ErrorContext| unsafe {

View file

@ -8,6 +8,7 @@ pub extern crate nixide_sys as sys;
pub(crate) mod errors;
mod init;
mod nix_settings;
mod stdext;
pub(crate) mod util;
mod verbosity;
@ -21,6 +22,7 @@ mod flake;
mod store;
pub use errors::{NixError, NixideError, NixideResult};
pub use nix_settings::{get_global_setting, set_global_setting};
pub use verbosity::{NixVerbosity, set_verbosity};
pub use version::NixVersion;

View file

@ -0,0 +1,37 @@
use std::ffi::c_void;
use crate::NixideResult;
use crate::errors::ErrorContext;
use crate::stdext::AsCPtr as _;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr as _;
/// # Note
/// This function is intentionally marked unsafe to discourage its use.
/// Please prefer [nixide::FlakeSettings] and [nixide::FetchersSettings].
///
pub unsafe fn get_global_setting<S: AsRef<str>>(key: S) -> NixideResult<String> {
let key = key.as_c_ptr()?;
wrap::nix_string_callback!(
|callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe {
sys::nix_setting_get(ctx.as_ptr(), key, Some(callback), userdata as *mut c_void);
}
)
}
/// # Note
/// This function is intentionally marked unsafe to discourage its use.
/// Please prefer [nixide::FlakeSettings] and [nixide::FetchersSettings].
///
pub unsafe fn set_global_setting<S: AsRef<str>, T: AsRef<str>>(
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(), key, value);
})
}

View file

@ -1,13 +1,4 @@
// XXX: TODO: should I add support for `nix_libstore_init_no_load_config`
// XXX: TODO: add support for nix_realised_string_* family of functions
// nix_realised_string_get_store_path
// nix_realised_string_get_store_path_count
// # nix_store_real_path
// # nix_store_is_valid_path
// # nix_store_get_version
// # nix_store_get_uri
// # nix_store_get_storedir
// # nix_store_copy_closure
// nix_libstore_init_no_load_config
#[cfg(test)]
@ -16,25 +7,38 @@ mod tests;
mod path;
pub use path::*;
use std::ffi::{c_char, c_void, CString};
use std::ffi::{c_char, c_void};
use std::path::PathBuf;
use std::ptr::{null, null_mut, NonNull};
use std::result::Result;
use std::ptr::{NonNull, null, null_mut};
use crate::errors::{new_nixide_error, ErrorContext};
use crate::stdext::CCharPtrExt;
use crate::NixideResult;
use crate::errors::ErrorContext;
use crate::stdext::{AsCPtr as _, CCharPtrExt as _};
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::{NixideError, NixideResult};
use crate::util::{panic_issue_call_failed, wrap};
/// Nix store for managing packages and derivations.
///
/// The store provides access to Nix packages, derivations, and store paths.
///
pub struct Store {
pub(crate) inner: NonNull<sys::Store>,
inner: NonNull<sys::Store>,
}
// 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<sys::Store> for Store {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::Store {
@ -63,13 +67,19 @@ impl Store {
/// # Errors
///
/// Returns an error if the store cannot be opened.
pub fn open(uri: Option<&str>) -> Result<Self, NixideError> {
let uri_ptr = match uri.map(CString::new) {
Some(Ok(c_uri)) => c_uri.as_ptr(),
Some(Err(_)) => Err(new_nixide_error!(StringNulByte))?,
None => null(),
};
///
pub fn open(uri: &str) -> NixideResult<Self> {
Self::open_ptr(uri.as_c_ptr()?)
}
/// Opens a connection to the default Nix store.
///
pub fn default() -> NixideResult<Self> {
Self::open_ptr(null())
}
#[inline]
fn open_ptr(uri_ptr: *const c_char) -> NixideResult<Self> {
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())
@ -95,6 +105,7 @@ impl Store {
/// # Errors
///
/// Returns an error if the path cannot be realized.
///
pub fn realise<F>(
&self,
path: &StorePath,
@ -107,11 +118,11 @@ impl Store {
-> 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().expect("IDK1");
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)
}).expect("IDK2");
}).unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
let store_path = StorePath { inner };
let callback = unsafe { (*userdata).inner };
@ -161,14 +172,16 @@ impl Store {
/// # Ok(())
/// # }
/// ```
pub fn store_path(&self, path: &str) -> Result<StorePath, NixideError> {
///
pub fn store_path(&self, path: &str) -> NixideResult<StorePath> {
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
pub fn version(&self) -> Result<String, NixideError> {
///
pub fn version(&self) -> NixideResult<String> {
wrap::nix_string_callback!(
|callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe {
sys::nix_store_get_version(
@ -182,7 +195,8 @@ impl Store {
}
/// Get the URI of a Nix store
pub fn uri(&self) -> Result<String, NixideError> {
///
pub fn uri(&self) -> NixideResult<String> {
wrap::nix_string_callback!(
|callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe {
sys::nix_store_get_uri(
@ -195,7 +209,7 @@ impl Store {
)
}
pub fn store_dir(&self) -> Result<PathBuf, NixideError> {
pub fn store_dir(&self) -> NixideResult<PathBuf> {
wrap::nix_pathbuf_callback!(
|callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe {
sys::nix_store_get_storedir(
@ -208,34 +222,26 @@ impl Store {
)
}
pub fn copy_closure_to(
&self,
dst_store: &Store,
store_path: &StorePath,
) -> Result<(), NixideError> {
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(),
); // semi-colon to return () and not i32
);
})
}
pub fn copy_closure_from(
&self,
src_store: &Store,
store_path: &StorePath,
) -> Result<(), NixideError> {
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.inner.as_ptr(),
store_path.as_ptr(),
);
}) // semi-colon to return () and not i32
})
}
}
@ -246,7 +252,3 @@ impl Drop for Store {
}
}
}
// SAFETY: Store can be shared between threads
unsafe impl Send for Store {}
unsafe impl Sync for Store {}

View file

@ -1,14 +1,14 @@
use std::ffi::{c_void, CString};
use std::ffi::{CString, c_void};
use std::path::PathBuf;
use std::ptr::NonNull;
use super::Store;
use crate::errors::{new_nixide_error, ErrorContext};
use crate::NixideResult;
use crate::errors::{ErrorContext, new_nixide_error};
use crate::sys;
use crate::util::panic_issue_call_failed;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::NixideResult;
/// A path in the Nix store.
///
@ -18,6 +18,25 @@ pub struct StorePath {
pub(crate) inner: NonNull<sys::StorePath>,
}
impl Clone for StorePath {
fn clone(&self) -> Self {
let inner = wrap::nix_ptr_fn!(|_| unsafe { sys::nix_store_path_clone(self.as_ptr()) })
.unwrap_or_else(|_| {
panic_issue_call_failed!("nix_store_path_clone returned None for valid path")
});
StorePath { inner }
}
}
impl Drop for StorePath {
fn drop(&mut self) {
unsafe {
sys::nix_store_path_free(self.as_ptr());
}
}
}
impl AsInnerPtr<sys::StorePath> for StorePath {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::StorePath {
@ -127,26 +146,3 @@ impl StorePath {
.is_ok()
}
}
impl Clone for StorePath {
fn clone(&self) -> Self {
let inner = wrap::nix_ptr_fn!(|_| unsafe { sys::nix_store_path_clone(self.as_ptr()) })
.unwrap_or_else(|_| {
panic_issue_call_failed!("nix_store_path_clone returned None for valid path")
});
StorePath { inner }
}
}
impl Drop for StorePath {
fn drop(&mut self) {
unsafe {
sys::nix_store_path_free(self.as_ptr());
}
}
}
// SAFETY: StorePath can be shared between threads
unsafe impl Send for StorePath {}
unsafe impl Sync for StorePath {}

View file

@ -2,39 +2,24 @@ use serial_test::serial;
use super::{Store, StorePath};
use crate::errors::ErrorContext;
use crate::init::LIBNIX_INIT_STATUS;
use crate::sys;
use crate::util::wrappers::AsInnerPtr as _;
#[test]
#[serial]
fn test_store_opening() {
let mut ctx = ErrorContext::new();
unsafe {
sys::nix_libutil_init(ctx.as_ptr());
ctx.pop()
.expect("nix_libutil_init failed with bad ErrorContext");
sys::nix_libstore_init(ctx.as_ptr());
ctx.pop()
.expect("nix_libstore_init failed with bad ErrorContext");
};
assert!(unsafe { matches!(LIBNIX_INIT_STATUS, Some(Ok(_))) });
let _store = Store::open(None).expect("Failed to open store");
let _store = Store::default().expect("Failed to open store");
}
#[test]
#[serial]
fn test_store_path_parse() {
let mut ctx = ErrorContext::new();
unsafe {
sys::nix_libutil_init(ctx.as_ptr());
ctx.pop()
.expect("nix_libutil_init failed with bad ErrorContext");
sys::nix_libstore_init(ctx.as_ptr());
ctx.pop()
.expect("nix_libstore_init failed with bad ErrorContext");
};
assert!(unsafe { matches!(LIBNIX_INIT_STATUS, Some(Ok(_))) });
let store = Store::open(None).expect("Failed to open store");
let store = Store::default().expect("Failed to open store");
// Try parsing a well-formed store path
let result = StorePath::fake_path(&store);
@ -44,17 +29,9 @@ fn test_store_path_parse() {
#[test]
#[serial]
fn test_store_path_clone() {
let mut ctx = ErrorContext::new();
unsafe {
sys::nix_libutil_init(ctx.as_ptr());
ctx.pop()
.expect("nix_libutil_init failed with bad ErrorContext");
sys::nix_libstore_init(ctx.as_ptr());
ctx.pop()
.expect("nix_libstore_init failed with bad ErrorContext");
};
assert!(unsafe { matches!(LIBNIX_INIT_STATUS, Some(Ok(_))) });
let store = Store::open(None).expect("Failed to open store");
let store = Store::default().expect("Failed to open store");
// Try to get a valid store path by parsing
let path = StorePath::fake_path(&store).expect("Failed to create `StorePath::fake_path`");