From 0ba06d0f2cc096a9d575b913b7f7414484384fb6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 26 Mar 2026 01:30:38 +1000 Subject: [PATCH] begin nixide::expr refactor --- nixide/src/errors/context.rs | 11 ++ nixide/src/expr/evalstate.rs | 59 ++++----- nixide/src/expr/evalstatebuilder.rs | 54 +++++---- nixide/src/expr/mod.rs | 4 + nixide/src/expr/realised_string.rs | 121 +++++++++++++++++++ nixide/src/expr/tests.rs | 13 +- nixide/src/expr/value.rs | 180 ++++++++-------------------- nixide/src/expr/values/attrs.rs | 0 nixide/src/expr/values/bool.rs | 70 +++++++++++ nixide/src/expr/values/external.rs | 0 nixide/src/expr/values/float.rs | 69 +++++++++++ nixide/src/expr/values/function.rs | 0 nixide/src/expr/values/int.rs | 69 +++++++++++ nixide/src/expr/values/list.rs | 0 nixide/src/expr/values/mod.rs | 29 +++++ nixide/src/expr/values/null.rs | 0 nixide/src/expr/values/path.rs | 0 nixide/src/expr/values/string.rs | 75 ++++++++++++ nixide/src/expr/values/thunk.rs | 0 nixide/src/expr/valuetype.rs | 6 +- nixide/src/lib.rs | 4 +- nixide/src/store/mod.rs | 11 ++ nixide/src/util/lazy_array.rs | 79 ++++++++++++ nixide/src/util/mod.rs | 36 +----- nixide/src/util/wrappers.rs | 45 +++---- 25 files changed, 681 insertions(+), 254 deletions(-) create mode 100644 nixide/src/expr/realised_string.rs create mode 100644 nixide/src/expr/values/attrs.rs create mode 100644 nixide/src/expr/values/bool.rs create mode 100644 nixide/src/expr/values/external.rs create mode 100644 nixide/src/expr/values/float.rs create mode 100644 nixide/src/expr/values/function.rs create mode 100644 nixide/src/expr/values/int.rs create mode 100644 nixide/src/expr/values/list.rs create mode 100644 nixide/src/expr/values/mod.rs create mode 100644 nixide/src/expr/values/null.rs create mode 100644 nixide/src/expr/values/path.rs create mode 100644 nixide/src/expr/values/string.rs create mode 100644 nixide/src/expr/values/thunk.rs create mode 100644 nixide/src/util/lazy_array.rs diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index d425b38..87d4b8e 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -68,9 +68,20 @@ pub(crate) struct ErrorContext { } impl AsInnerPtr for ErrorContext { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::nix_c_context { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_c_context { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_c_context { + unsafe { self.inner.as_mut() } + } } impl Into> for &ErrorContext { diff --git a/nixide/src/expr/evalstate.rs b/nixide/src/expr/evalstate.rs index bdb7884..304409a 100644 --- a/nixide/src/expr/evalstate.rs +++ b/nixide/src/expr/evalstate.rs @@ -7,8 +7,9 @@ use crate::errors::new_nixide_error; use super::Value; use crate::errors::ErrorContext; use crate::sys; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; -use crate::{NixideError, Store}; +use crate::{NixideResult, Store}; /// Nix evaluation state for evaluating expressions. /// @@ -18,14 +19,24 @@ pub struct EvalState { inner: NonNull, // XXX: TODO: is an `Arc` necessary or just a `Store` - #[allow(dead_code)] store: Arc, } impl AsInnerPtr for EvalState { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::EvalState { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::EvalState { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::EvalState { + unsafe { self.inner.as_mut() } + } } impl EvalState { @@ -34,6 +45,11 @@ impl EvalState { Self { inner, store } } + #[inline] + pub(crate) unsafe fn store_ref(&self) -> &Store { + self.store.as_ref() + } + /// Evaluate a Nix expression from a string. /// /// # Arguments @@ -44,24 +60,15 @@ impl EvalState { /// # Errors /// /// Returns an error if evaluation fails. - pub fn eval_from_string(&self, expr: &str, path: &str) -> Result { + pub fn eval_from_string(&self, expr: &str, path: &str) -> NixideResult { let expr_c = CString::new(expr).or(Err(new_nixide_error!(StringNulByte)))?; let path_c = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; - let ctx = ErrorContext::new(); // Allocate value for result - // XXX: TODO: refactor this code to use `nixide::Value` - let value_ptr = unsafe { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) }; - let value = match ctx.peak() { - Some(err) => Err(err), - None => match NonNull::new(value_ptr) { - Some(inner) => Ok(Value { inner }), - None => Err(new_nixide_error!(NullPtr)), - }, - }?; + let value = self.new_value()?; // Evaluate expression - unsafe { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_expr_eval_from_string( ctx.as_ptr(), self.as_ptr(), @@ -69,11 +76,8 @@ impl EvalState { path_c.as_ptr(), value.as_ptr(), ); - }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(value), - } + value + }) } /// Allocate a new value. @@ -81,16 +85,13 @@ impl EvalState { /// # Errors /// /// Returns an error if value allocation fails. - pub fn alloc_value(&self) -> Result { - let ctx = ErrorContext::new(); - let value_ptr = unsafe { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => match NonNull::new(value_ptr) { - Some(inner) => Ok(Value { inner }), - None => Err(new_nixide_error!(NullPtr)), - }, - } + pub(self) fn new_value(&self) -> NixideResult { + // XXX: TODO: should this function be `Value::new` instead? + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) + })?; + + Ok(Value::new(inner, self)) } } diff --git a/nixide/src/expr/evalstatebuilder.rs b/nixide/src/expr/evalstatebuilder.rs index 25cf117..82908dc 100644 --- a/nixide/src/expr/evalstatebuilder.rs +++ b/nixide/src/expr/evalstatebuilder.rs @@ -2,8 +2,9 @@ use std::ptr::NonNull; use std::sync::Arc; use super::EvalState; -use crate::errors::{new_nixide_error, ErrorContext, NixideError}; +use crate::errors::{ErrorContext, NixideResult}; use crate::sys; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; use crate::Store; @@ -16,6 +17,23 @@ pub struct EvalStateBuilder { store: Arc, } +impl AsInnerPtr for EvalStateBuilder { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_eval_state_builder { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_eval_state_builder { + unsafe { self.inner.as_mut() } + } +} + impl EvalStateBuilder { /// Create a new evaluation state builder. /// @@ -26,12 +44,10 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the builder cannot be created. - pub fn new(store: &Arc) -> Result { - // SAFETY: store context and store are valid - let builder_ptr = - unsafe { sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr()) }; - - let inner = NonNull::new(builder_ptr).ok_or(new_nixide_error!(NullPtr))?; + pub fn new(store: &Arc) -> NixideResult { + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_eval_state_builder_new(ctx.as_ptr(), store.as_ptr()) + })?; Ok(EvalStateBuilder { inner, @@ -44,29 +60,19 @@ impl EvalStateBuilder { /// # Errors /// /// Returns an error if the evaluation state cannot be built. - pub fn build(self) -> Result { - let ctx = ErrorContext::new(); + pub fn build(self) -> NixideResult { // Load configuration first - unsafe { sys::nix_eval_state_builder_load(ctx.as_ptr(), self.as_ptr()) }; - if let Some(err) = ctx.peak() { - return Err(err); - } + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_eval_state_builder_load(ctx.as_ptr(), self.as_ptr()) + })?; // Build the state - let state_ptr = unsafe { sys::nix_eval_state_build(ctx.as_ptr(), self.as_ptr()) }; - if let Some(err) = ctx.peak() { - return Err(err); - } + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_eval_state_build(ctx.as_ptr(), self.as_ptr()) + })?; - let inner = NonNull::new(state_ptr).ok_or(new_nixide_error!(NullPtr))?; - - // The builder is consumed here - its Drop will clean up Ok(EvalState::new(inner, self.store.clone())) } - - pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder { - self.inner.as_ptr() - } } impl Drop for EvalStateBuilder { diff --git a/nixide/src/expr/mod.rs b/nixide/src/expr/mod.rs index ab4ae81..8487311 100644 --- a/nixide/src/expr/mod.rs +++ b/nixide/src/expr/mod.rs @@ -3,10 +3,14 @@ mod tests; mod evalstate; mod evalstatebuilder; +mod realised_string; mod value; +mod values; mod valuetype; pub use evalstate::EvalState; pub use evalstatebuilder::EvalStateBuilder; +pub use realised_string::RealisedString; pub use value::Value; +pub use values::{NixInt, NixValue}; pub use valuetype::ValueType; diff --git a/nixide/src/expr/realised_string.rs b/nixide/src/expr/realised_string.rs new file mode 100644 index 0000000..5be5f8b --- /dev/null +++ b/nixide/src/expr/realised_string.rs @@ -0,0 +1,121 @@ +use std::ffi::c_char; +use std::ptr::NonNull; +use std::sync::Arc; + +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::{panic_issue_call_failed, wrap}; +use crate::{EvalState, NixideResult, StorePath}; + +pub struct RealisedString { + inner: NonNull, + // pub path: LazyCell StorePath>>, + pub path: StorePath, + pub children: LazyArray StorePath>, usize) -> StorePath>>, +} + +impl AsInnerPtr for RealisedString { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_realised_string { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_realised_string { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_realised_string { + unsafe { self.inner.as_mut() } + } +} + +impl Drop for RealisedString { + fn drop(&mut self) { + unsafe { + sys::nix_realised_string_free(self.as_ptr()); + } + } +} + +impl RealisedString { + /// Realise a string context. + /// + /// This will + /// - realise the store paths referenced by the string's context, and + /// - perform the replacement of placeholders. + /// - create temporary garbage collection roots for the store paths, for + /// the lifetime of the current process. + /// - log to stderr + /// + /// # Arguments + /// + /// * value - Nix value, which must be a string + /// * state - Nix evaluator state + /// * isIFD - If true, disallow derivation outputs if setting `allow-import-from-derivation` is false. + /// You should set this to true when this call is part of a primop. + /// You should set this to false when building for your application's purpose. + /// + /// # Returns + /// + /// NULL if failed, or a new nix_realised_string, which must be freed with nix_realised_string_free + pub fn new(value: &NixString, state: &Arc) -> NixideResult { + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_string_realise( + ctx.as_ptr(), + state.as_ptr(), + value.as_ptr(), + false, // don't copy more + ) + })?; + + fn delegate( + inner: &LazyArray StorePath>>, + index: usize, + ) -> StorePath { + // XXX: TODO + // inner[index] + StorePath::fake_path(unsafe { state.store_ref() }).unwrap() + } + + let size = unsafe { sys::nix_realised_string_get_store_path_count(inner.as_ptr()) }; + + Ok(Self { + inner, + path: Self::parse_path(inner.as_ptr(), state), + // children: LazyArray::new(size, delegate as fn(usize) -> StorePath), + children: LazyArray:: StorePath>>::new( + size, + Box::new(delegate), + ), + }) + } + + fn parse_path( + realised_string: *mut sys::nix_realised_string, + state: &Arc, + ) -> StorePath { + let buffer_ptr = unsafe { sys::nix_realised_string_get_buffer_start(realised_string) }; + let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_string) }; + + let path_str = (buffer_ptr as *const c_char) + .to_utf8_string_n(buffer_size) + .unwrap_or_else(|err| { + panic_issue_call_failed!( + "`sys::nix_realised_string_get_buffer_(start|size)` invalid UTF-8 ({})", + err + ) + }); + StorePath::parse(unsafe { 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 + ) + }) + } +} diff --git a/nixide/src/expr/tests.rs b/nixide/src/expr/tests.rs index 1bcc04a..702b44d 100644 --- a/nixide/src/expr/tests.rs +++ b/nixide/src/expr/tests.rs @@ -3,14 +3,12 @@ use std::sync::Arc; use serial_test::serial; use super::{EvalStateBuilder, ValueType}; -use crate::errors::ErrorContext; use crate::Store; #[test] #[serial] fn test_eval_state_builder() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let _state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() @@ -21,8 +19,7 @@ fn test_eval_state_builder() { #[test] #[serial] fn test_simple_evaluation() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() @@ -39,8 +36,7 @@ fn test_simple_evaluation() { #[test] #[serial] fn test_value_types() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() @@ -71,8 +67,7 @@ fn test_value_types() { #[test] #[serial] fn test_value_formatting() { - let ctx = Arc::new(ErrorContext::new()); - let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store")); + let store = Arc::new(Store::open(None).expect("Failed to open store")); let state = EvalStateBuilder::new(&store) .expect("Failed to create builder") .build() diff --git a/nixide/src/expr/value.rs b/nixide/src/expr/value.rs index 93f3787..ce7237c 100644 --- a/nixide/src/expr/value.rs +++ b/nixide/src/expr/value.rs @@ -2,30 +2,40 @@ use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ptr::NonNull; use super::{EvalState, ValueType}; -use crate::errors::{new_nixide_error, ErrorContext, NixideError}; +use crate::errors::{ErrorContext, NixideResult}; use crate::sys; -use crate::util::wrappers::{AsInnerPtr, FromC as _}; -use crate::util::AsErr; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; /// A Nix value /// /// This represents any value in the Nix language, including primitives, /// collections, and functions. -pub struct Value { +pub struct Value<'a> { pub(crate) inner: NonNull, + state: &'a EvalState, } -impl AsInnerPtr for Value { +impl<'a> AsInnerPtr for Value<'a> { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::nix_value { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } } -impl Value { - pub(crate) unsafe fn new(inner: *mut sys::Value) -> Self { - Value { - inner: NonNull::new(inner).unwrap(), - } +impl<'a> Value<'a> { + pub(crate) fn new(inner: NonNull, state: &'a EvalState) -> Self { + Value { inner, state } } /// Force evaluation of this value. @@ -35,12 +45,11 @@ impl Value { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force(&mut self, state: &EvalState) -> Result<(), NixideError> { + pub fn force(&mut self) -> NixideResult<()> { // XXX: TODO: move force and force_deep to the EvalState - let ctx = ErrorContext::new(); - - unsafe { sys::nix_value_force(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; - ctx.peak() + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_value_force(ctx.as_ptr(), self.state.as_ptr(), self.as_ptr()); + }) } /// Force deep evaluation of this value. @@ -50,132 +59,39 @@ impl Value { /// # Errors /// /// Returns an error if evaluation fails. - pub fn force_deep(&mut self, state: &EvalState) -> Result<(), NixideError> { - let ctx = ErrorContext::new(); - - unsafe { sys::nix_value_force_deep(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) }; - ctx.peak() + pub fn force_deep(&mut self) -> NixideResult<()> { + // XXX: TODO: move force and force_deep to the EvalState + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_value_force_deep(ctx.as_ptr(), self.state.as_ptr(), self.as_ptr()); + }) } /// Get the type of this value. #[must_use] pub fn value_type(&self) -> ValueType { - let ctx = ErrorContext::new(); - let value_type = - unsafe { ValueType::from_c(sys::nix_get_type(ctx.as_ptr(), self.as_ptr())) }; // NOTE: an error here only occurs if `nix_get_type` catches an error, // NOTE: which in turn only happens if the `sys::nix_value*` is a null pointer // NOTE: or points to an uninitialised `nix_value` struct. - ctx.peak().unwrap_or_else(|_| panic!("TODO im sleepy rn")); - value_type + ValueType::from({ + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_type(ctx.as_ptr(), self.as_ptr()) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)) + }) } - fn expect_type(&self, expected: ValueType) -> Result<(), NixideError> { - let got = self.value_type(); - if got != expected { - return Err(new_nixide_error!( - InvalidType, - expected.to_string(), - got.to_string() - )); - } - Ok(()) - } - - /// Convert this value to an integer. - /// - /// # Errors - /// - /// Returns an error if the value is not an integer. - pub fn as_int(&self) -> Result { - self.expect_type(ValueType::Int)?; - - let ctx = ErrorContext::new(); - let result = unsafe { sys::nix_get_int(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(result), - } - } - - /// Convert this value to a float. - /// - /// # Errors - /// - /// Returns an error if the value is not a float. - pub fn as_float(&self) -> Result { - self.expect_type(ValueType::Float)?; - - let ctx = ErrorContext::new(); - let result = unsafe { sys::nix_get_float(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(result), - } - } - - /// Convert this value to a boolean. - /// - /// # Errors - /// - /// Returns an error if the value is not a boolean. - pub fn as_bool(&self) -> Result { - self.expect_type(ValueType::Bool)?; - - let ctx = ErrorContext::new(); - let result = unsafe { sys::nix_get_bool(ctx.as_ptr(), self.as_ptr()) }; - match ctx.peak() { - Some(err) => Err(err), - None => Ok(result), - } - } - - /// Convert this value to a string. - /// - /// # Errors - /// - /// Returns an error if the value is not a string. - pub fn as_string(&self) -> Result { - self.expect_type(ValueType::String)?; - - let ctx = ErrorContext::new(); - - // For string values, we need to use realised string API - let realised_str = unsafe { - sys::nix_string_realise( - ctx.as_ptr(), - self.state.as_ptr(), - self.as_ptr(), - false, // don't copy more - ) - }; - - if realised_str.is_null() { - return Err(new_nixide_error!(NullPtr)); - } - - let buffer_start = unsafe { sys::nix_realised_string_get_buffer_start(realised_str) }; - let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_str) }; - if buffer_start.is_null() { - // Clean up realised string - unsafe { - sys::nix_realised_string_free(realised_str); - } - return Err(new_nixide_error!(NullPtr)); - } - - let bytes = unsafe { std::slice::from_raw_parts(buffer_start.cast::(), buffer_size) }; - let string = std::str::from_utf8(bytes) - .map_err(|_| new_nixide_error!(StringNotUtf8))? - .to_owned(); - - // Clean up realised string - unsafe { - sys::nix_realised_string_free(realised_str); - } - - Ok(string) - } + // XXX: TODO: rewrite `expr/value.rs` to make this redundant + // fn expect_type(&self, expected: ValueType) -> NixideResult<()> { + // let got = self.value_type(); + // if got != expected { + // return Err(new_nixide_error!( + // InvalidType, + // expected.to_string(), + // got.to_string() + // )); + // } + // Ok(()) + // } /// Format this value as Nix syntax. /// @@ -186,7 +102,7 @@ impl Value { /// /// Returns an error if the value cannot be converted to a string /// representation. - pub fn to_nix_string(&self) -> Result { + pub fn to_nix_string(&self) -> NixideResult { match self.value_type() { ValueType::Int => Ok(self.as_int()?.to_string()), ValueType::Float => Ok(self.as_float()?.to_string()), diff --git a/nixide/src/expr/values/attrs.rs b/nixide/src/expr/values/attrs.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/bool.rs b/nixide/src/expr/values/bool.rs new file mode 100644 index 0000000..fd5598b --- /dev/null +++ b/nixide/src/expr/values/bool.rs @@ -0,0 +1,70 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +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; + +pub struct NixBool { + inner: NonNull, + value: bool, +} + +impl Display for NixBool { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixBool { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixBool(${})", self.value) + } +} + +impl AsInnerPtr for NixBool { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixBool { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_BOOL + } + + fn new(inner: NonNull) -> Self { + let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_bool(ctx.as_ptr(), inner.as_ptr()) + }) + .unwrap_or_else(|err| { + panic_issue_call_failed!("`sys::nix_get_bool` failed for valid `NixBool` ({})", err) + }); + + Self { inner, value } + } +} + +impl NixBool { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &bool { + &self.value + } +} diff --git a/nixide/src/expr/values/external.rs b/nixide/src/expr/values/external.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/float.rs b/nixide/src/expr/values/float.rs new file mode 100644 index 0000000..84c5539 --- /dev/null +++ b/nixide/src/expr/values/float.rs @@ -0,0 +1,69 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::NixValue; +use crate::errors::ErrorContext; +use crate::sys; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; + +pub struct NixFloat { + inner: NonNull, + value: f64, +} + +impl Display for NixFloat { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixFloat { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixFloat(${})", self.value()) + } +} + +impl AsInnerPtr for NixFloat { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixFloat { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_FLOAT + } + + fn new(inner: NonNull) -> Self { + let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_float(ctx.as_ptr(), inner.as_ptr()) + }) + .unwrap_or_else(|err| { + panic_issue_call_failed!("`sys::nix_get_float` failed for valid `NixFloat` ({})", err) + }); + + Self { inner, value } + } +} + +impl NixFloat { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &f64 { + &self.value + } +} diff --git a/nixide/src/expr/values/function.rs b/nixide/src/expr/values/function.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/int.rs b/nixide/src/expr/values/int.rs new file mode 100644 index 0000000..e9a6264 --- /dev/null +++ b/nixide/src/expr/values/int.rs @@ -0,0 +1,69 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::NixValue; +use crate::errors::ErrorContext; +use crate::sys; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; + +pub struct NixInt { + inner: NonNull, + value: i64, +} + +impl Display for NixInt { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixInt { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixInt(${})", self.value()) + } +} + +impl AsInnerPtr for NixInt { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixInt { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_INT + } + + fn new(inner: NonNull) -> 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!("`sys::nix_get_int` failed for valid `NixInt` ({})", err) + }); + + Self { inner, value } + } +} + +impl NixInt { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &i64 { + &self.value + } +} diff --git a/nixide/src/expr/values/list.rs b/nixide/src/expr/values/list.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/mod.rs b/nixide/src/expr/values/mod.rs new file mode 100644 index 0000000..bc503d1 --- /dev/null +++ b/nixide/src/expr/values/mod.rs @@ -0,0 +1,29 @@ +mod attrs; +mod bool; +mod external; +mod float; +mod function; +mod int; +mod list; +mod null; +mod path; +mod string; +mod thunk; + +pub use bool::NixBool; +pub use float::NixFloat; +pub use int::NixInt; +pub use string::NixString; + +use std::fmt::{Debug, Display}; +use std::ptr::NonNull; + +use crate::sys; +use crate::util::wrappers::AsInnerPtr; + +pub trait NixValue: Display + Debug + AsInnerPtr { + /// TODO + fn get_enum_id(&self) -> sys::ValueType; + + fn new(inner: NonNull) -> Self; +} diff --git a/nixide/src/expr/values/null.rs b/nixide/src/expr/values/null.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/path.rs b/nixide/src/expr/values/path.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/values/string.rs b/nixide/src/expr/values/string.rs new file mode 100644 index 0000000..78fc272 --- /dev/null +++ b/nixide/src/expr/values/string.rs @@ -0,0 +1,75 @@ +use std::cell::LazyCell; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; + +use super::NixValue; +use crate::expr::RealisedString; +use crate::util::panic_issue_call_failed; +use crate::util::wrap; +use crate::util::wrappers::AsInnerPtr; +use crate::{sys, NixideResult}; + +pub struct NixString { + inner: NonNull, + value: String, +} + +impl Display for NixString { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixString { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixString(\"${}\")", self.value()) + } +} + +impl AsInnerPtr for NixString { + #[inline] + unsafe fn as_ptr(&self) -> *mut sys::nix_value { + self.inner.as_ptr() + } + + #[inline] + unsafe fn as_ref(&self) -> &sys::nix_value { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::nix_value { + unsafe { self.inner.as_mut() } + } +} + +impl NixValue for NixString { + #[inline] + fn get_enum_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_STRING + } + + fn new(inner: NonNull) -> Self { + // wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + // sys::nix_get_int(ctx.as_ptr(), inner.as_ptr()) + // }) + // .unwrap_or_else(|err| { + // panic_issue_call_failed!( + // "`sys::nix_get_int` failed for valid `NixString` ({})", + // err + // ) + // }) + // }; + + Self { inner, value } + } +} + +impl NixString { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &String { + &self.value + } +} diff --git a/nixide/src/expr/values/thunk.rs b/nixide/src/expr/values/thunk.rs new file mode 100644 index 0000000..e69de29 diff --git a/nixide/src/expr/valuetype.rs b/nixide/src/expr/valuetype.rs index 62ed8cd..ad7b194 100644 --- a/nixide/src/expr/valuetype.rs +++ b/nixide/src/expr/valuetype.rs @@ -1,6 +1,6 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use crate::{sys, util::wrappers::FromC}; +use crate::sys; /// Nix value types. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -29,8 +29,8 @@ pub enum ValueType { External, } -impl FromC for ValueType { - unsafe fn from_c(value_type: sys::ValueType) -> Self { +impl From for ValueType { + fn from(value_type: sys::ValueType) -> Self { match value_type { sys::ValueType_NIX_TYPE_THUNK => ValueType::Thunk, sys::ValueType_NIX_TYPE_INT => ValueType::Int, diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 5b363b4..c8f3b58 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -5,7 +5,7 @@ pub extern crate libc; pub extern crate nixide_sys as sys; pub(crate) mod errors; -// mod expr; +mod expr; // mod flake; mod stdext; mod store; @@ -14,7 +14,7 @@ mod verbosity; mod version; pub use errors::{NixError, NixideError, NixideResult}; -// pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; +pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; pub use store::{Store, StorePath}; pub use verbosity::NixVerbosity; pub use version::NixVersion; diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 0d52edb..4bca2a8 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -36,9 +36,20 @@ pub struct Store { } impl AsInnerPtr for Store { + #[inline] unsafe fn as_ptr(&self) -> *mut sys::Store { self.inner.as_ptr() } + + #[inline] + unsafe fn as_ref(&self) -> &sys::Store { + unsafe { self.inner.as_ref() } + } + + #[inline] + unsafe fn as_mut(&mut self) -> &mut sys::Store { + unsafe { self.inner.as_mut() } + } } impl Store { diff --git a/nixide/src/util/lazy_array.rs b/nixide/src/util/lazy_array.rs new file mode 100644 index 0000000..2808314 --- /dev/null +++ b/nixide/src/util/lazy_array.rs @@ -0,0 +1,79 @@ +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug)] +pub struct LazyArray +where + F: Fn(usize) -> T, +{ + inner: Rc>>>, + size: usize, + delegate: F, +} + +impl LazyArray +where + F: Fn(usize) -> T, +{ + pub fn new(size: usize, delegate: F) -> LazyArray { + let mut vec = Vec::with_capacity(size); + for _ in 0..size { + vec.push(None); + } + + LazyArray { + inner: Rc::new(RefCell::new(vec)), + size, + delegate, + } + } + + /// Returns `None` if `index < self.size` otherwise always succeeds + /// (unless of course the callback you supply panics). + /// + // pub fn get(&mut self, index: usize) -> Option<&T> { + // // let x = self.inner.get(index).copied().and_then(|value| match value { + // // Some(value) => Some(value), + // // None => { + // // // store the value first + // // let value = (self.delegate)(index); + // // self.inner[index] = Some(value); + + // // // now get a reference to it + // // if let Some(v) = &self.inner[index] { + // // return Some(v); + // // } + // // None + // // } + // // }) + // match self.inner.clone().borrow().get(index) { + // Some(Some(value)) => Some(value), + // Some(None) => { + // let mut inner = self.inner.clone().borrow_mut(); + // // store the value first + // inner[index] = Some((self.delegate)(index)); + + // // now get a reference to it + // inner[index].as_ref() + // } + // None => None, + // } + // } + pub fn get(&mut self, index: usize) -> Option> { + if index >= self.size { + return None; + } + + // let inner = self.inner.borrow(); + if let Some(value) = self.inner.borrow()[index].as_ref() { + return Some(Rc::new(value)); + } + + // drop(inner); // explicitly drop the borrow + + let value = (self.delegate)(index); + self.inner.borrow_mut()[index] = Some(value); + + Some(Rc::new(self.inner.borrow()[index].unwrap())) + } +} diff --git a/nixide/src/util/mod.rs b/nixide/src/util/mod.rs index 2302b41..f9ec55d 100644 --- a/nixide/src/util/mod.rs +++ b/nixide/src/util/mod.rs @@ -1,38 +1,8 @@ #[macro_use] pub mod panic; +mod lazy_array; pub(crate) mod wrap; pub mod wrappers; -pub(crate) use panic::*; - -// use crate::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(()), -// } -// } -// } - -// pub trait AsInnerPtr { -// /// Get a pointer to the underlying (`inner`) `libnix` C struct. -// /// -// /// # Safety -// /// -// /// Although this function isn't inherently `unsafe`, it is -// /// marked as such intentionally to force calls to be wrapped -// /// in `unsafe` blocks for clarity. -// unsafe fn as_ptr(&self) -> *mut T; -// } - -// pub trait FromC { -// /// Creates a new instance of [Self] from the underlying -// /// libnix C type [T]. -// unsafe fn from_c(value: T) -> Self; -// } +pub(crate) use lazy_array::LazyArray; +pub(crate) use panic::{panic_issue, panic_issue_call_failed}; diff --git a/nixide/src/util/wrappers.rs b/nixide/src/util/wrappers.rs index ab43cce..6fa0378 100644 --- a/nixide/src/util/wrappers.rs +++ b/nixide/src/util/wrappers.rs @@ -1,26 +1,5 @@ -// use crate::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(()), -// } -// } -// } -// -// pub trait FromC { -// /// Creates a new instance of [Self] from the underlying -// /// libnix C type [T]. -// unsafe fn from_c(value: T) -> Self; -// } - pub trait AsInnerPtr { - /// Get a pointer to the underlying (`inner`) `libnix` C struct. + /// Acquires the underlying pointer to the inner `libnix` C struct. /// /// # Safety /// @@ -28,4 +7,26 @@ pub trait AsInnerPtr { /// marked as such intentionally to force calls to be wrapped /// in `unsafe` blocks for clarity. unsafe fn as_ptr(&self) -> *mut T; + + /// Returns a shared reference to the inner `libnix` C struct. + /// + /// For the mutable counterpart see [AsInnerPtr::as_mut]. + /// + /// # Safety + /// + /// Although this function isn't inherently `unsafe`, it is + /// marked as such intentionally to force calls to be wrapped + /// in `unsafe` blocks for clarity. + unsafe fn as_ref(&self) -> &T; + + /// Returns a unique reference to the inner `libnix` C struct. + /// + /// For the shared counterpart see [AsInnerPtr::as_ref]. + /// + /// # Safety + /// + /// Although this function isn't inherently `unsafe`, it is + /// marked as such intentionally to force calls to be wrapped + /// in `unsafe` blocks for clarity. + unsafe fn as_mut(&mut self) -> &mut T; }