diff --git a/nixide/src/errors/context.rs b/nixide/src/errors/context.rs index 6ea266e..2ab2227 100644 --- a/nixide/src/errors/context.rs +++ b/nixide/src/errors/context.rs @@ -137,7 +137,9 @@ impl ErrorContext { pub fn new() -> Self { match NonNull::new(unsafe { sys::nix_c_context_create() }) { Some(inner) => ErrorContext { inner }, - None => panic!("[nixide] CRITICAL FAILURE: Out-Of-Memory condition reached - `sys::nix_c_context_create` allocation failed!"), + None => panic!( + "[nixide] CRITICAL FAILURE: Out-Of-Memory condition reached - `sys::nix_c_context_create` allocation failed!" + ), } } diff --git a/nixide/src/expr/evalstate.rs b/nixide/src/expr/evalstate.rs index 1c6bb28..b73b74d 100644 --- a/nixide/src/expr/evalstate.rs +++ b/nixide/src/expr/evalstate.rs @@ -4,11 +4,11 @@ use std::sync::Arc; use crate::errors::new_nixide_error; -use super::Value; +use super::{NixValue, Value}; use crate::errors::ErrorContext; use crate::sys; -use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; use crate::{NixideResult, Store}; /// Nix evaluation state for evaluating expressions. @@ -65,7 +65,11 @@ impl EvalState { let path_c = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; // Allocate value for result - let value = self.new_value()?; + // XXX: TODO: create a method for this (``) + let value = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); // Evaluate expression wrap::nix_fn!(|ctx: &ErrorContext| unsafe { @@ -78,6 +82,7 @@ impl EvalState { ); value }) + .map(|ptr| Value::from((ptr, self))) } /// Allocate a new value. @@ -91,7 +96,7 @@ impl EvalState { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) })?; - Ok(Value::new(inner, self)) + Ok(Value::from((inner, self))) } } diff --git a/nixide/src/expr/evalstatebuilder.rs b/nixide/src/expr/evalstatebuilder.rs index d9dca5a..6163ee5 100644 --- a/nixide/src/expr/evalstatebuilder.rs +++ b/nixide/src/expr/evalstatebuilder.rs @@ -1,13 +1,13 @@ -use std::ffi::{c_char, CStr, CString}; +use std::ffi::{CString, c_char}; use std::ptr::{self, NonNull}; use std::sync::Arc; use super::EvalState; +use crate::Store; use crate::errors::{ErrorContext, NixideResult}; use crate::sys; use crate::util::wrappers::AsInnerPtr; use crate::util::{panic_issue_call_failed, wrap}; -use crate::Store; /// Builder for Nix evaluation state. /// diff --git a/nixide/src/expr/mod.rs b/nixide/src/expr/mod.rs index 8487311..57f6808 100644 --- a/nixide/src/expr/mod.rs +++ b/nixide/src/expr/mod.rs @@ -4,13 +4,9 @@ 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; +pub use values::*; diff --git a/nixide/src/expr/value.rs b/nixide/src/expr/value.rs index d3c7d4c..6d6bff8 100644 --- a/nixide/src/expr/value.rs +++ b/nixide/src/expr/value.rs @@ -7,7 +7,10 @@ use crate::sys; use crate::util::wrappers::AsInnerPtr; use crate::util::{panic_issue_call_failed, wrap}; -pub use crate::expr::values::{NixBool, NixFloat, NixInt, NixString, NixValue}; +pub use crate::expr::values::{ + NixAttrs, NixBool, NixExternal, NixFloat, NixFunction, NixInt, NixList, NixNull, NixPath, + NixString, NixThunk, NixValue, +}; /// A Nix value /// @@ -28,31 +31,39 @@ pub use crate::expr::values::{NixBool, NixFloat, NixInt, NixString, NixValue}; /// pub const ValueType_NIX_TYPE_EXTERNAL: ValueType = 10; /// ``` pub enum Value { - // Thunk(NixThunk), + /// Unevaluated expression + /// + /// Thunks often contain an expression and closure, but may contain other + /// representations too. + /// + /// Their state is mutable, unlike that of the other types. + Thunk(NixThunk), Int(NixInt), Float(NixFloat), Bool(NixBool), String(NixString), - // Path(NixPath), - // Null(NixNull), - // Attrs(NixAttrs), - // List(NixList), - // Function(NixFunction), - // External(NixExternal), + Path(NixPath), + Null(NixNull), + Attrs(NixAttrs), + List(NixList), + Function(NixFunction), + External(NixExternal), } -impl<'a> Value<'a> { - pub(crate) fn new(inner: NonNull, state: &'a EvalState) -> Self { - Value { inner, state } - } - - /// Force evaluation of this value. +impl Value { + /// Forces the evaluation of a Nix value. /// - /// If the value is a thunk, this will evaluate it to its final form. + /// The Nix interpreter is lazy, and not-yet-evaluated values can be + /// of type NIX_TYPE_THUNK instead of their actual value. /// - /// # Errors + /// This function mutates such a `nix_value`, so that, if successful, it has its final type. /// - /// Returns an error if evaluation fails. + /// @param[out] context Optional, stores error information + /// @param[in] state The state of the evaluation. + /// @param[in,out] value The Nix value to force. + /// @post value is not of type NIX_TYPE_THUNK + /// @return NIX_OK if the force operation was successful, an error code + /// otherwise. pub fn force(&mut self) -> NixideResult<()> { // XXX: TODO: move force and force_deep to the EvalState wrap::nix_fn!(|ctx: &ErrorContext| unsafe { @@ -112,21 +123,21 @@ impl<'a> Value<'a> { /// representation. 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()), - ValueType::Bool => Ok(if self.as_bool()? { + | ValueType::Int => Ok(self.as_int()?.to_string()), + | ValueType::Float => Ok(self.as_float()?.to_string()), + | ValueType::Bool => Ok(if self.as_bool()? { "true".to_string() } else { "false".to_string() }), - ValueType::String => Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\""))), - ValueType::Null => Ok("null".to_string()), - ValueType::Attrs => Ok("{ }".to_string()), - ValueType::List => Ok("[ ]".to_string()), - ValueType::Function => Ok("".to_string()), - ValueType::Path => Ok("".to_string()), - ValueType::Thunk => Ok("".to_string()), - ValueType::External => Ok("".to_string()), + | ValueType::String => Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\""))), + | ValueType::Null => Ok("null".to_string()), + | ValueType::Attrs => Ok("{ }".to_string()), + | ValueType::List => Ok("[ ]".to_string()), + | ValueType::Function => Ok("".to_string()), + | ValueType::Path => Ok("".to_string()), + | ValueType::Thunk => Ok("".to_string()), + | ValueType::External => Ok("".to_string()), } } } @@ -143,41 +154,41 @@ impl Drop for Value { impl Display for Value { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self.value_type() { - ValueType::Int => { + | ValueType::Int => { if let Ok(val) = self.as_int() { write!(f, "{val}") } else { write!(f, "") } - } - ValueType::Float => { + }, + | ValueType::Float => { if let Ok(val) = self.as_float() { write!(f, "{val}") } else { write!(f, "") } - } - ValueType::Bool => { + }, + | ValueType::Bool => { if let Ok(val) = self.as_bool() { write!(f, "{val}") } else { write!(f, "") } - } - ValueType::String => { + }, + | ValueType::String => { if let Ok(val) = self.as_string() { write!(f, "{val}") } else { write!(f, "") } - } - ValueType::Null => write!(f, "null"), - ValueType::Attrs => write!(f, "{{ }}"), - ValueType::List => write!(f, "[ ]"), - ValueType::Function => write!(f, ""), - ValueType::Path => write!(f, ""), - ValueType::Thunk => write!(f, ""), - ValueType::External => write!(f, ""), + }, + | ValueType::Null => write!(f, "null"), + | ValueType::Attrs => write!(f, "{{ }}"), + | ValueType::List => write!(f, "[ ]"), + | ValueType::Function => write!(f, ""), + | ValueType::Path => write!(f, ""), + | ValueType::Thunk => write!(f, ""), + | ValueType::External => write!(f, ""), } } } @@ -186,41 +197,41 @@ impl Debug for Value { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { let value_type = self.value_type(); match value_type { - ValueType::Int => { + | ValueType::Int => { if let Ok(val) = self.as_int() { write!(f, "Value::Int({val})") } else { write!(f, "Value::Int()") } - } - ValueType::Float => { + }, + | ValueType::Float => { if let Ok(val) = self.as_float() { write!(f, "Value::Float({val})") } else { write!(f, "Value::Float()") } - } - ValueType::Bool => { + }, + | ValueType::Bool => { if let Ok(val) = self.as_bool() { write!(f, "Value::Bool({val})") } else { write!(f, "Value::Bool()") } - } - ValueType::String => { + }, + | ValueType::String => { if let Ok(val) = self.as_string() { write!(f, "Value::String({val:?})") } else { write!(f, "Value::String()") } - } - ValueType::Null => write!(f, "Value::Null"), - ValueType::Attrs => write!(f, "Value::Attrs({{ }})"), - ValueType::List => write!(f, "Value::List([ ])"), - ValueType::Function => write!(f, "Value::Function()"), - ValueType::Path => write!(f, "Value::Path()"), - ValueType::Thunk => write!(f, "Value::Thunk()"), - ValueType::External => write!(f, "Value::External()"), + }, + | ValueType::Null => write!(f, "Value::Null"), + | ValueType::Attrs => write!(f, "Value::Attrs({{ }})"), + | ValueType::List => write!(f, "Value::List([ ])"), + | ValueType::Function => write!(f, "Value::Function()"), + | ValueType::Path => write!(f, "Value::Path()"), + | ValueType::Thunk => write!(f, "Value::Thunk()"), + | ValueType::External => write!(f, "Value::External()"), } } } diff --git a/nixide/src/expr/values/attrs.rs b/nixide/src/expr/values/attrs.rs index e69de29..43d8884 100644 --- a/nixide/src/expr/values/attrs.rs +++ b/nixide/src/expr/values/attrs.rs @@ -0,0 +1,207 @@ +use std::cell::RefCell; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::{self, NonNull}; +use std::rc::Rc; + +use super::{NixThunk, NixValue, Value}; +use crate::errors::{ErrorContext, NixideError}; +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, + state: Rc>, + len: u32, +} + +impl Drop for NixAttrs { + fn drop(&mut self) { + let ctx = ErrorContext::new(); + unsafe { + sys::nix_value_decref(ctx.as_ptr(), self.as_ptr()); + } + } +} + +impl Display for NixAttrs { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixAttrs { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixAttrs") + } +} + +impl AsInnerPtr for NixAttrs { + #[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 NixAttrs { + #[inline] + fn type_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_ATTRS + } + + fn from(inner: NonNull, state: Rc>) -> 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 } + } +} + +impl NixAttrs { + #[inline] + pub fn len(&self) -> u32 { + self.len + } + + pub fn get_idx(&self, index: u32) -> Option<(String, Value)> { + if index >= self.len { + return None; + } + + let name_ptr = ptr::null_mut(); + + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_attr_byidx( + ctx.as_ptr(), + self.as_ptr(), + self.state.borrow().as_ptr(), + index, + name_ptr, + ) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let name = (unsafe { *name_ptr }) + .to_utf8_string() + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let value = Value::from((inner, self.state.clone())); + + Some((name, value)) + } + + pub fn get_idx_lazy(&self, index: u32) -> Option<(String, NixThunk)> { + if index >= self.len { + return None; + } + + let name_ptr = ptr::null_mut(); + + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_attr_byidx_lazy( + ctx.as_ptr(), + self.as_ptr(), + self.state.borrow().as_ptr(), + index, + name_ptr, + ) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let name = (unsafe { *name_ptr }) + .to_utf8_string() + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let value = ::from(inner, self.state.clone()); + + Some((name, value)) + } + + pub fn get_name_idx(&self, index: u32) -> Option { + if index >= self.len { + return None; + } + + let name_ptr = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_attr_name_byidx( + ctx.as_ptr(), + self.as_ptr(), + self.state.borrow().as_ptr(), + index, + ) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let name = name_ptr + .to_utf8_string() + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + Some(name) + } + + pub fn get(&self, name: T) -> Option + where + T: AsRef, + { + let result = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_attr_byname( + ctx.as_ptr(), + self.as_ptr(), + self.state.borrow().as_ptr(), + name.as_ref() + .into_c_ptr() + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)), + ) + }); + + match result { + Ok(inner) => Some(Value::from((inner, self.state.clone()))), + + Err(NixideError::NixError { + err: NixError::KeyNotFound(_), + .. + }) => None, + Err(err) => panic_issue_call_failed!("{}", err), + } + } + + pub fn get_lazy(&self, name: T) -> Option + where + T: AsRef, + { + let result = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_attr_byname_lazy( + ctx.as_ptr(), + self.as_ptr(), + self.state.borrow().as_ptr(), + name.as_ref() + .into_c_ptr() + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)), + ) + }); + + match result { + Ok(inner) => Some(::from(inner, self.state.clone())), + + Err(NixideError::NixError { + err: NixError::KeyNotFound(_), + .. + }) => None, + Err(err) => panic_issue_call_failed!("{}", err), + } + } +} diff --git a/nixide/src/expr/values/bool.rs b/nixide/src/expr/values/bool.rs index 1b81f0f..9aa17e5 100644 --- a/nixide/src/expr/values/bool.rs +++ b/nixide/src/expr/values/bool.rs @@ -1,15 +1,18 @@ +use std::cell::RefCell; 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, + state: Rc>, value: bool, } @@ -53,11 +56,11 @@ impl AsInnerPtr for NixBool { impl NixValue for NixBool { #[inline] - fn id(&self) -> sys::ValueType { + fn type_id(&self) -> sys::ValueType { sys::ValueType_NIX_TYPE_BOOL } - fn new(inner: NonNull) -> Self { + fn from(inner: NonNull, state: Rc>) -> Self { let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_get_bool(ctx.as_ptr(), inner.as_ptr()) }) @@ -65,7 +68,11 @@ impl NixValue for NixBool { panic_issue_call_failed!("`sys::nix_get_bool` failed for valid `NixBool` ({})", err) }); - Self { inner, value } + Self { + inner, + state, + value, + } } } diff --git a/nixide/src/expr/values/float.rs b/nixide/src/expr/values/float.rs index 7eb8314..82b635a 100644 --- a/nixide/src/expr/values/float.rs +++ b/nixide/src/expr/values/float.rs @@ -1,14 +1,17 @@ +use std::cell::RefCell; 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::wrappers::AsInnerPtr; use crate::util::{panic_issue_call_failed, wrap}; +use crate::{EvalState, sys}; pub struct NixFloat { inner: NonNull, + state: Rc>, value: f64, } @@ -52,11 +55,11 @@ impl AsInnerPtr for NixFloat { impl NixValue for NixFloat { #[inline] - fn id(&self) -> sys::ValueType { + fn type_id(&self) -> sys::ValueType { sys::ValueType_NIX_TYPE_FLOAT } - fn new(inner: NonNull) -> Self { + fn from(inner: NonNull, state: Rc>) -> Self { let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_get_float(ctx.as_ptr(), inner.as_ptr()) }) @@ -64,7 +67,11 @@ impl NixValue for NixFloat { panic_issue_call_failed!("`sys::nix_get_float` failed for valid `NixFloat` ({})", err) }); - Self { inner, value } + Self { + inner, + state, + value, + } } } diff --git a/nixide/src/expr/values/function.rs b/nixide/src/expr/values/function.rs index e69de29..895128f 100644 --- a/nixide/src/expr/values/function.rs +++ b/nixide/src/expr/values/function.rs @@ -0,0 +1,130 @@ +use std::cell::RefCell; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; +use std::rc::Rc; + +use super::{NixThunk, NixValue, Value}; +use crate::errors::ErrorContext; +use crate::stdext::SliceExt; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; +use crate::{EvalState, sys}; + +pub struct NixFunction { + inner: NonNull, + state: Rc>, + value: i64, +} + +impl Drop for NixFunction { + fn drop(&mut self) { + let ctx = ErrorContext::new(); + unsafe { + sys::nix_value_decref(ctx.as_ptr(), self.as_ptr()); + } + } +} + +impl Display for NixFunction { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixFunction { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixFunction") + } +} + +impl AsInnerPtr for NixFunction { + #[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 NixFunction { + #[inline] + fn type_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_FUNCTION + } + + fn from(inner: NonNull, state: Rc>) -> 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)); + + Self { + inner, + state, + value, + } + } +} + +// impl Fn<(Value,)> for NixFunction { +// extern "rust-call" fn call(&self, args: (Value,)) -> Value { +// args.0 +// } +// } + +impl NixFunction { + fn call(&self, arg: &T) -> Value + where + T: NixValue, + { + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_alloc_value(ctx.as_ptr(), self.state.borrow().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.as_ptr(), + arg.as_ptr(), + inner.as_ptr(), + ); + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + Value::from((inner, self.state.clone())) + } + + fn call_many(&self, args: &[&T]) -> Value + where + T: NixValue, + { + let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_alloc_value(ctx.as_ptr(), self.state.borrow().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.as_ptr(), + args.len(), + args.into_c_array(), + inner.as_ptr(), + ); + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + Value::from((inner, self.state.clone())) + } +} diff --git a/nixide/src/expr/values/int.rs b/nixide/src/expr/values/int.rs index bb5ab66..3b381da 100644 --- a/nixide/src/expr/values/int.rs +++ b/nixide/src/expr/values/int.rs @@ -1,14 +1,17 @@ +use std::cell::RefCell; 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::wrappers::AsInnerPtr; use crate::util::{panic_issue_call_failed, wrap}; +use crate::{EvalState, sys}; pub struct NixInt { inner: NonNull, + state: Rc>, value: i64, } @@ -52,19 +55,21 @@ impl AsInnerPtr for NixInt { impl NixValue for NixInt { #[inline] - fn id(&self) -> sys::ValueType { + fn type_id(&self) -> sys::ValueType { sys::ValueType_NIX_TYPE_INT } - fn new(inner: NonNull) -> Self { + fn from(inner: NonNull, state: Rc>) -> 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) - }); + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); - Self { inner, value } + Self { + inner, + state, + value, + } } } diff --git a/nixide/src/expr/values/list.rs b/nixide/src/expr/values/list.rs index e69de29..96dd9f5 100644 --- a/nixide/src/expr/values/list.rs +++ b/nixide/src/expr/values/list.rs @@ -0,0 +1,136 @@ +use std::cell::RefCell; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr::NonNull; +use std::rc::Rc; + +use super::{NixThunk, NixValue, Value}; +use crate::errors::ErrorContext; +use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; +use crate::{EvalState, sys}; + +pub struct NixList { + inner: NonNull, + state: Rc>, + value: i64, +} + +impl Drop for NixList { + fn drop(&mut self) { + let ctx = ErrorContext::new(); + unsafe { + sys::nix_value_decref(ctx.as_ptr(), self.as_ptr()); + } + } +} + +impl Display for NixList { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixList { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixList") + } +} + +impl AsInnerPtr for NixList { + #[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 NixList { + #[inline] + fn type_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_LIST + } + + fn from(inner: NonNull, state: Rc>) -> 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)); + + Self { + inner, + state, + value, + } + } +} + +impl NixList { + /// Forces the evaluation on all elements of the list. + /// + fn as_vec(&self) -> Vec { + // XXX: TODO: should I just return a LazyArray instead? + let mut value = Vec::new(); + for i in 0..self.len() { + value.push(self.get(i)); + } + + value + } + + fn as_vec_lazy(&self) -> Vec { + // XXX: TODO: should I just return a LazyArray instead? + let mut value = Vec::new(); + for i in 0..self.len() { + value.push(self.get_lazy(i)); + } + + value + } + + /// Get the length of a list. This function preserves + /// laziness and does not evaluate the internal fields. + /// + fn len(&self) -> u32 { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_list_size(ctx.as_ptr(), self.as_ptr()) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)) + } + + 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, + ) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + Value::from((inner, self.state.clone())) + } + + 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, + ) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + ::from(inner, self.state.clone()) + } +} diff --git a/nixide/src/expr/values/mod.rs b/nixide/src/expr/values/mod.rs index 8224b40..7d2ff7d 100644 --- a/nixide/src/expr/values/mod.rs +++ b/nixide/src/expr/values/mod.rs @@ -10,21 +10,145 @@ mod path; mod string; mod thunk; +pub use attrs::NixAttrs; pub use bool::NixBool; +pub use external::NixExternal; +// pub use failed::NixFailed; // only in latest nix version pub use float::NixFloat; +pub use function::NixFunction; pub use int::NixInt; +pub use list::NixList; +pub use null::NixNull; +pub use path::NixPath; pub use string::NixString; +pub use thunk::NixThunk; +use std::cell::RefCell; use std::fmt::{Debug, Display}; use std::ptr::NonNull; +use std::rc::Rc; +use crate::EvalState; +use crate::errors::ErrorContext; use crate::sys; +use crate::sys::{ + ValueType_NIX_TYPE_ATTRS, ValueType_NIX_TYPE_BOOL, ValueType_NIX_TYPE_EXTERNAL, + ValueType_NIX_TYPE_FLOAT, ValueType_NIX_TYPE_FUNCTION, ValueType_NIX_TYPE_INT, + ValueType_NIX_TYPE_LIST, ValueType_NIX_TYPE_NULL, ValueType_NIX_TYPE_PATH, + ValueType_NIX_TYPE_STRING, ValueType_NIX_TYPE_THUNK, +}; use crate::util::wrappers::AsInnerPtr; +use crate::util::{panic_issue_call_failed, wrap}; pub trait NixValue: Drop + Display + Debug + AsInnerPtr { /// TODO - fn id(&self) -> sys::ValueType; + fn type_id(&self) -> sys::ValueType; /// TODO - fn new(inner: NonNull) -> Self; + fn from(inner: NonNull, state: Rc>) -> Self; +} + +/// A Nix value +/// +/// This represents any value in the Nix language, including primitives, +/// collections, and functions. +/// +/// # Nix C++ API Internals +/// +/// ```cpp +/// typedef enum { +/// NIX_TYPE_THUNK = 0, +/// NIX_TYPE_INT = 1, +/// NIX_TYPE_FLOAT = 2, +/// NIX_TYPE_BOOL = 3, +/// NIX_TYPE_STRING = 4, +/// NIX_TYPE_PATH = 5, +/// NIX_TYPE_NULL = 6, +/// NIX_TYPE_ATTRS = 7, +/// NIX_TYPE_LIST = 8, +/// NIX_TYPE_FUNCTION = 9, +/// NIX_TYPE_EXTERNAL = 10, +/// NIX_TYPE_FAILED = 11, +///} ValueType; +/// ``` +/// +pub enum Value { + /// Unevaluated expression + /// + /// Thunks often contain an expression and closure, but may contain other + /// representations too. + /// + /// Their state is mutable, unlike that of the other types. + Thunk(NixThunk), + + /// TODO + Int(NixInt), + + /// TODO + Float(NixFloat), + + /// TODO + Bool(NixBool), + + /// TODO + String(NixString), + + /// TODO + Path(NixPath), + + /// TODO + Null(NixNull), + + /// TODO + Attrs(NixAttrs), + + /// TODO + List(NixList), + + /// TODO + Function(NixFunction), + + /// TODO + External(NixExternal), + + /// TODO + Failed(NixFailed), +} + +impl From<(NonNull, Rc>)> for Value { + fn from(value: (NonNull, Rc>)) -> 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()) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + let type_id = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_type(ctx.as_ptr(), inner.as_ptr()) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + match type_id { + ValueType_NIX_TYPE_THUNK => Value::Thunk(::from(inner, state)), + ValueType_NIX_TYPE_INT => Value::Int(::from(inner, state)), + ValueType_NIX_TYPE_FLOAT => Value::Float(::from(inner, state)), + ValueType_NIX_TYPE_BOOL => Value::Bool(::from(inner, state)), + ValueType_NIX_TYPE_STRING => Value::String(::from(inner, state)), + ValueType_NIX_TYPE_PATH => Value::Path(::from(inner, state)), + ValueType_NIX_TYPE_NULL => Value::Null(::from(inner, state)), + ValueType_NIX_TYPE_ATTRS => Value::Attrs(::from(inner, state)), + ValueType_NIX_TYPE_LIST => Value::List(::from(inner, state)), + ValueType_NIX_TYPE_FUNCTION => { + Value::Function(::from(inner, state)) + }, + ValueType_NIX_TYPE_EXTERNAL => { + Value::External(::from(inner, state)) + }, + // | sys::ValueType_NIX_TYPE_FAILED => { + // Value::Failed(::from(inner, state)) + // }, + _ => unreachable!(), + } + } } diff --git a/nixide/src/expr/values/null.rs b/nixide/src/expr/values/null.rs index e69de29..7b92254 100644 --- a/nixide/src/expr/values/null.rs +++ b/nixide/src/expr/values/null.rs @@ -0,0 +1,63 @@ +use std::cell::RefCell; +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::util::wrappers::AsInnerPtr; +use crate::{EvalState, sys}; + +pub struct NixNull { + inner: NonNull, + state: Rc>, +} + +impl Drop for NixNull { + fn drop(&mut self) { + let ctx = ErrorContext::new(); + unsafe { + sys::nix_value_decref(ctx.as_ptr(), self.as_ptr()); + } + } +} + +impl Display for NixNull { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "null") + } +} + +impl Debug for NixNull { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixNull") + } +} + +impl AsInnerPtr for NixNull { + #[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 NixNull { + #[inline] + fn type_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_NULL + } + + fn from(inner: NonNull, state: Rc>) -> Self { + Self { inner, state } + } +} diff --git a/nixide/src/expr/values/path.rs b/nixide/src/expr/values/path.rs index e69de29..145b2d2 100644 --- a/nixide/src/expr/values/path.rs +++ b/nixide/src/expr/values/path.rs @@ -0,0 +1,88 @@ +use std::cell::RefCell; +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::util::panic_issue_call_failed; +use crate::util::wrap; +use crate::util::wrappers::AsInnerPtr; +use crate::{EvalState, sys}; + +pub struct NixPath { + inner: NonNull, + state: Rc>, + value: PathBuf, +} + +impl Drop for NixPath { + fn drop(&mut self) { + let ctx = ErrorContext::new(); + unsafe { + sys::nix_value_decref(ctx.as_ptr(), self.as_ptr()); + } + } +} + +impl Display for NixPath { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixPath { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixPath(\"${}\")", self.value().display()) + } +} + +impl AsInnerPtr for NixPath { + #[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 NixPath { + #[inline] + fn type_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_PATH + } + + fn from(inner: NonNull, state: Rc>) -> Self { + let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_get_path_string(ctx.as_ptr(), inner.as_ptr()) + }) + .and_then(CCharPtrExt::to_utf8_string) + .map(PathBuf::from) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + Self { + inner, + state, + value, + } + } +} + +impl NixPath { + /// Returns a shared reference to the underlying value. + /// + #[inline] + fn value(&self) -> &PathBuf { + &self.value + } +} diff --git a/nixide/src/expr/values/string.rs b/nixide/src/expr/values/string.rs index d0c0f4c..2ceeb41 100644 --- a/nixide/src/expr/values/string.rs +++ b/nixide/src/expr/values/string.rs @@ -1,17 +1,19 @@ -use std::cell::LazyCell; +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::expr::RealisedString; use crate::util::panic_issue_call_failed; use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; -use crate::{sys, NixideResult}; +use crate::{EvalState, sys}; pub struct NixString { inner: NonNull, + state: Rc>, value: String, } @@ -55,23 +57,28 @@ impl AsInnerPtr for NixString { impl NixValue for NixString { #[inline] - fn id(&self) -> sys::ValueType { + fn type_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 - // ) - // }) - // }; + fn from(inner: NonNull, state: Rc>) -> Self { + let value = wrap::nix_string_callback!( + |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe { + sys::nix_get_string( + ctx.as_ptr(), + inner.as_ptr(), + Some(callback), + userdata as *mut c_void, + ); + } + ) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); - Self { inner, value } + Self { + inner, + state, + value, + } } } diff --git a/nixide/src/expr/values/thunk.rs b/nixide/src/expr/values/thunk.rs index e69de29..19a5470 100644 --- a/nixide/src/expr/values/thunk.rs +++ b/nixide/src/expr/values/thunk.rs @@ -0,0 +1,84 @@ +use std::cell::RefCell; +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; +use crate::util::{panic_issue_call_failed, wrap}; + +pub struct NixThunk { + inner: NonNull, + state: Rc>, +} + +impl Drop for NixThunk { + fn drop(&mut self) { + wrap::nix_fn!(|ctx: &ErrorContext| unsafe { + sys::nix_value_decref(ctx.as_ptr(), self.as_ptr()); + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)) + } +} + +impl Display for NixThunk { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "") + } +} + +impl Debug for NixThunk { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "NixThunk") + } +} + +impl AsInnerPtr for NixThunk { + #[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 NixThunk { + #[inline] + fn type_id(&self) -> sys::ValueType { + sys::ValueType_NIX_TYPE_THUNK + } + + fn from(inner: NonNull, state: Rc>) -> Self { + Self { inner, state } + } +} + +impl NixThunk { + fn to_inner(self) -> NonNull { + self.inner + } + + 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(), + ) + }) + .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)); + + Value::from((self.inner, self.state.to_owned())) + } +} diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 2acbdd1..4601253 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -1,5 +1,7 @@ // #![warn(missing_docs)] +#![cfg_attr(nightly, feature(fn_traits))] + pub extern crate libc; pub extern crate nixide_sys as sys; @@ -17,11 +19,11 @@ mod flake; mod store; pub use errors::{NixError, NixideError, NixideResult}; -pub use verbosity::{set_verbosity, NixVerbosity}; +pub use verbosity::{NixVerbosity, set_verbosity}; pub use version::NixVersion; #[cfg(feature = "expr")] -pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; +pub use expr::{EvalState, EvalStateBuilder, Value}; #[cfg(feature = "store")] pub use store::{Store, StorePath}; diff --git a/nixide/src/util/panic.rs b/nixide/src/util/panic.rs index b9e5e95..0772e31 100644 --- a/nixide/src/util/panic.rs +++ b/nixide/src/util/panic.rs @@ -9,10 +9,10 @@ macro_rules! panic_issue { macro_rules! panic_issue_call_failed { () => {{ - crate::util::panic_issue!("[nixide] call to `{}` failed", $crate::stdext::debug_name!()) + crate::util::panic_issue!("[nixide] call to `{}` failed", ::stdext::debug_name!()) }}; ($($arg:expr),*) => {{ - crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", $crate::stdext::debug_name!(), format!($($arg),*)) + crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", ::stdext::debug_name!(), format!($($arg),*)) }}; }