implement NixValue types

This commit is contained in:
do butterflies cry? 2026-03-29 00:02:47 +10:00
parent 84c51f5b16
commit 0d8468c71e
Signed by: cry
GPG key ID: F68745A836CA0412
18 changed files with 981 additions and 107 deletions

View file

@ -137,7 +137,9 @@ impl ErrorContext {
pub fn new() -> Self { pub fn new() -> Self {
match NonNull::new(unsafe { sys::nix_c_context_create() }) { match NonNull::new(unsafe { sys::nix_c_context_create() }) {
Some(inner) => ErrorContext { inner }, 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!"
),
} }
} }

View file

@ -4,11 +4,11 @@ use std::sync::Arc;
use crate::errors::new_nixide_error; use crate::errors::new_nixide_error;
use super::Value; use super::{NixValue, Value};
use crate::errors::ErrorContext; use crate::errors::ErrorContext;
use crate::sys; use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr; use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap};
use crate::{NixideResult, Store}; use crate::{NixideResult, Store};
/// Nix evaluation state for evaluating expressions. /// Nix evaluation state for evaluating expressions.
@ -65,7 +65,11 @@ impl EvalState {
let path_c = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?; let path_c = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?;
// Allocate value for result // 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 // Evaluate expression
wrap::nix_fn!(|ctx: &ErrorContext| unsafe { wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
@ -78,6 +82,7 @@ impl EvalState {
); );
value value
}) })
.map(|ptr| Value::from((ptr, self)))
} }
/// Allocate a new value. /// Allocate a new value.
@ -91,7 +96,7 @@ impl EvalState {
sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr())
})?; })?;
Ok(Value::new(inner, self)) Ok(Value::from((inner, self)))
} }
} }

View file

@ -1,13 +1,13 @@
use std::ffi::{c_char, CStr, CString}; use std::ffi::{CString, c_char};
use std::ptr::{self, NonNull}; use std::ptr::{self, NonNull};
use std::sync::Arc; use std::sync::Arc;
use super::EvalState; use super::EvalState;
use crate::Store;
use crate::errors::{ErrorContext, NixideResult}; use crate::errors::{ErrorContext, NixideResult};
use crate::sys; use crate::sys;
use crate::util::wrappers::AsInnerPtr; use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap}; use crate::util::{panic_issue_call_failed, wrap};
use crate::Store;
/// Builder for Nix evaluation state. /// Builder for Nix evaluation state.
/// ///

View file

@ -4,13 +4,9 @@ mod tests;
mod evalstate; mod evalstate;
mod evalstatebuilder; mod evalstatebuilder;
mod realised_string; mod realised_string;
mod value;
mod values; mod values;
mod valuetype;
pub use evalstate::EvalState; pub use evalstate::EvalState;
pub use evalstatebuilder::EvalStateBuilder; pub use evalstatebuilder::EvalStateBuilder;
pub use realised_string::RealisedString; pub use realised_string::RealisedString;
pub use value::Value; pub use values::*;
pub use values::{NixInt, NixValue};
pub use valuetype::ValueType;

View file

@ -7,7 +7,10 @@ use crate::sys;
use crate::util::wrappers::AsInnerPtr; use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap}; 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 /// 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 const ValueType_NIX_TYPE_EXTERNAL: ValueType = 10;
/// ``` /// ```
pub enum Value { 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), Int(NixInt),
Float(NixFloat), Float(NixFloat),
Bool(NixBool), Bool(NixBool),
String(NixString), String(NixString),
// Path(NixPath), Path(NixPath),
// Null(NixNull), Null(NixNull),
// Attrs(NixAttrs), Attrs(NixAttrs),
// List(NixList), List(NixList),
// Function(NixFunction), Function(NixFunction),
// External(NixExternal), External(NixExternal),
} }
impl<'a> Value<'a> { impl Value {
pub(crate) fn new(inner: NonNull<sys::nix_value>, state: &'a EvalState) -> Self { /// Forces the evaluation of a Nix value.
Value { inner, state }
}
/// Force evaluation of this 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<()> { pub fn force(&mut self) -> NixideResult<()> {
// XXX: TODO: move force and force_deep to the EvalState // XXX: TODO: move force and force_deep to the EvalState
wrap::nix_fn!(|ctx: &ErrorContext| unsafe { wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
@ -112,21 +123,21 @@ impl<'a> Value<'a> {
/// representation. /// representation.
pub fn to_nix_string(&self) -> NixideResult<String> { pub fn to_nix_string(&self) -> NixideResult<String> {
match self.value_type() { match self.value_type() {
ValueType::Int => Ok(self.as_int()?.to_string()), | ValueType::Int => Ok(self.as_int()?.to_string()),
ValueType::Float => Ok(self.as_float()?.to_string()), | ValueType::Float => Ok(self.as_float()?.to_string()),
ValueType::Bool => Ok(if self.as_bool()? { | ValueType::Bool => Ok(if self.as_bool()? {
"true".to_string() "true".to_string()
} else { } else {
"false".to_string() "false".to_string()
}), }),
ValueType::String => Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\""))), | ValueType::String => Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\""))),
ValueType::Null => Ok("null".to_string()), | ValueType::Null => Ok("null".to_string()),
ValueType::Attrs => Ok("{ <attrs> }".to_string()), | ValueType::Attrs => Ok("{ <attrs> }".to_string()),
ValueType::List => Ok("[ <list> ]".to_string()), | ValueType::List => Ok("[ <list> ]".to_string()),
ValueType::Function => Ok("<function>".to_string()), | ValueType::Function => Ok("<function>".to_string()),
ValueType::Path => Ok("<path>".to_string()), | ValueType::Path => Ok("<path>".to_string()),
ValueType::Thunk => Ok("<thunk>".to_string()), | ValueType::Thunk => Ok("<thunk>".to_string()),
ValueType::External => Ok("<external>".to_string()), | ValueType::External => Ok("<external>".to_string()),
} }
} }
} }
@ -143,41 +154,41 @@ impl Drop for Value {
impl Display for Value { impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self.value_type() { match self.value_type() {
ValueType::Int => { | ValueType::Int => {
if let Ok(val) = self.as_int() { if let Ok(val) = self.as_int() {
write!(f, "{val}") write!(f, "{val}")
} else { } else {
write!(f, "<int error>") write!(f, "<int error>")
} }
} },
ValueType::Float => { | ValueType::Float => {
if let Ok(val) = self.as_float() { if let Ok(val) = self.as_float() {
write!(f, "{val}") write!(f, "{val}")
} else { } else {
write!(f, "<float error>") write!(f, "<float error>")
} }
} },
ValueType::Bool => { | ValueType::Bool => {
if let Ok(val) = self.as_bool() { if let Ok(val) = self.as_bool() {
write!(f, "{val}") write!(f, "{val}")
} else { } else {
write!(f, "<bool error>") write!(f, "<bool error>")
} }
} },
ValueType::String => { | ValueType::String => {
if let Ok(val) = self.as_string() { if let Ok(val) = self.as_string() {
write!(f, "{val}") write!(f, "{val}")
} else { } else {
write!(f, "<string error>") write!(f, "<string error>")
} }
} },
ValueType::Null => write!(f, "null"), | ValueType::Null => write!(f, "null"),
ValueType::Attrs => write!(f, "{{ <attrs> }}"), | ValueType::Attrs => write!(f, "{{ <attrs> }}"),
ValueType::List => write!(f, "[ <list> ]"), | ValueType::List => write!(f, "[ <list> ]"),
ValueType::Function => write!(f, "<function>"), | ValueType::Function => write!(f, "<function>"),
ValueType::Path => write!(f, "<path>"), | ValueType::Path => write!(f, "<path>"),
ValueType::Thunk => write!(f, "<thunk>"), | ValueType::Thunk => write!(f, "<thunk>"),
ValueType::External => write!(f, "<external>"), | ValueType::External => write!(f, "<external>"),
} }
} }
} }
@ -186,41 +197,41 @@ impl Debug for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let value_type = self.value_type(); let value_type = self.value_type();
match value_type { match value_type {
ValueType::Int => { | ValueType::Int => {
if let Ok(val) = self.as_int() { if let Ok(val) = self.as_int() {
write!(f, "Value::Int({val})") write!(f, "Value::Int({val})")
} else { } else {
write!(f, "Value::Int(<error>)") write!(f, "Value::Int(<error>)")
} }
} },
ValueType::Float => { | ValueType::Float => {
if let Ok(val) = self.as_float() { if let Ok(val) = self.as_float() {
write!(f, "Value::Float({val})") write!(f, "Value::Float({val})")
} else { } else {
write!(f, "Value::Float(<error>)") write!(f, "Value::Float(<error>)")
} }
} },
ValueType::Bool => { | ValueType::Bool => {
if let Ok(val) = self.as_bool() { if let Ok(val) = self.as_bool() {
write!(f, "Value::Bool({val})") write!(f, "Value::Bool({val})")
} else { } else {
write!(f, "Value::Bool(<error>)") write!(f, "Value::Bool(<error>)")
} }
} },
ValueType::String => { | ValueType::String => {
if let Ok(val) = self.as_string() { if let Ok(val) = self.as_string() {
write!(f, "Value::String({val:?})") write!(f, "Value::String({val:?})")
} else { } else {
write!(f, "Value::String(<error>)") write!(f, "Value::String(<error>)")
} }
} },
ValueType::Null => write!(f, "Value::Null"), | ValueType::Null => write!(f, "Value::Null"),
ValueType::Attrs => write!(f, "Value::Attrs({{ <attrs> }})"), | ValueType::Attrs => write!(f, "Value::Attrs({{ <attrs> }})"),
ValueType::List => write!(f, "Value::List([ <list> ])"), | ValueType::List => write!(f, "Value::List([ <list> ])"),
ValueType::Function => write!(f, "Value::Function(<function>)"), | ValueType::Function => write!(f, "Value::Function(<function>)"),
ValueType::Path => write!(f, "Value::Path(<path>)"), | ValueType::Path => write!(f, "Value::Path(<path>)"),
ValueType::Thunk => write!(f, "Value::Thunk(<thunk>)"), | ValueType::Thunk => write!(f, "Value::Thunk(<thunk>)"),
ValueType::External => write!(f, "Value::External(<external>)"), | ValueType::External => write!(f, "Value::External(<external>)"),
} }
} }
} }

View file

@ -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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
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, "<attrs>")
}
}
impl Debug for NixAttrs {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "NixAttrs")
}
}
impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>, state: Rc<RefCell<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 }
}
}
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 = <NixThunk as NixValue>::from(inner, self.state.clone());
Some((name, value))
}
pub fn get_name_idx(&self, index: u32) -> Option<String> {
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<T>(&self, name: T) -> Option<Value>
where
T: AsRef<str>,
{
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<T>(&self, name: T) -> Option<NixThunk>
where
T: AsRef<str>,
{
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(<NixThunk as NixValue>::from(inner, self.state.clone())),
Err(NixideError::NixError {
err: NixError::KeyNotFound(_),
..
}) => None,
Err(err) => panic_issue_call_failed!("{}", err),
}
}
}

View file

@ -1,15 +1,18 @@
use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull; use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue; use super::NixValue;
use crate::errors::ErrorContext; use crate::errors::ErrorContext;
use crate::sys;
use crate::util::panic_issue_call_failed; use crate::util::panic_issue_call_failed;
use crate::util::wrap; use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr; use crate::util::wrappers::AsInnerPtr;
use crate::{EvalState, sys};
pub struct NixBool { pub struct NixBool {
inner: NonNull<sys::nix_value>, inner: NonNull<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: bool, value: bool,
} }
@ -53,11 +56,11 @@ impl AsInnerPtr<sys::nix_value> for NixBool {
impl NixValue for NixBool { impl NixValue for NixBool {
#[inline] #[inline]
fn id(&self) -> sys::ValueType { fn type_id(&self) -> sys::ValueType {
sys::ValueType_NIX_TYPE_BOOL sys::ValueType_NIX_TYPE_BOOL
} }
fn new(inner: NonNull<sys::nix_value>) -> Self { fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_bool(ctx.as_ptr(), inner.as_ptr()) 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) panic_issue_call_failed!("`sys::nix_get_bool` failed for valid `NixBool` ({})", err)
}); });
Self { inner, value } Self {
inner,
state,
value,
}
} }
} }

View file

@ -1,14 +1,17 @@
use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull; use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue; use super::NixValue;
use crate::errors::ErrorContext; use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrappers::AsInnerPtr; use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap}; use crate::util::{panic_issue_call_failed, wrap};
use crate::{EvalState, sys};
pub struct NixFloat { pub struct NixFloat {
inner: NonNull<sys::nix_value>, inner: NonNull<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: f64, value: f64,
} }
@ -52,11 +55,11 @@ impl AsInnerPtr<sys::nix_value> for NixFloat {
impl NixValue for NixFloat { impl NixValue for NixFloat {
#[inline] #[inline]
fn id(&self) -> sys::ValueType { fn type_id(&self) -> sys::ValueType {
sys::ValueType_NIX_TYPE_FLOAT sys::ValueType_NIX_TYPE_FLOAT
} }
fn new(inner: NonNull<sys::nix_value>) -> Self { fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_float(ctx.as_ptr(), inner.as_ptr()) 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) panic_issue_call_failed!("`sys::nix_get_float` failed for valid `NixFloat` ({})", err)
}); });
Self { inner, value } Self {
inner,
state,
value,
}
} }
} }

View file

@ -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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
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, "<function>")
}
}
impl Debug for NixFunction {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "NixFunction")
}
}
impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>, state: Rc<RefCell<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));
Self {
inner,
state,
value,
}
}
}
// impl Fn<(Value,)> for NixFunction {
// extern "rust-call" fn call(&self, args: (Value,)) -> Value {
// args.0
// }
// }
impl NixFunction {
fn call<T>(&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<T>(&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()))
}
}

View file

@ -1,14 +1,17 @@
use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull; use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue; use super::NixValue;
use crate::errors::ErrorContext; use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrappers::AsInnerPtr; use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap}; use crate::util::{panic_issue_call_failed, wrap};
use crate::{EvalState, sys};
pub struct NixInt { pub struct NixInt {
inner: NonNull<sys::nix_value>, inner: NonNull<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: i64, value: i64,
} }
@ -52,19 +55,21 @@ impl AsInnerPtr<sys::nix_value> for NixInt {
impl NixValue for NixInt { impl NixValue for NixInt {
#[inline] #[inline]
fn id(&self) -> sys::ValueType { fn type_id(&self) -> sys::ValueType {
sys::ValueType_NIX_TYPE_INT sys::ValueType_NIX_TYPE_INT
} }
fn new(inner: NonNull<sys::nix_value>) -> Self { fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> Self {
let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe { let value = wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_get_int(ctx.as_ptr(), inner.as_ptr()) sys::nix_get_int(ctx.as_ptr(), inner.as_ptr())
}) })
.unwrap_or_else(|err| { .unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
panic_issue_call_failed!("`sys::nix_get_int` failed for valid `NixInt` ({})", err)
});
Self { inner, value } Self {
inner,
state,
value,
}
} }
} }

View file

@ -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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
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, "<list>")
}
}
impl Debug for NixList {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "NixList")
}
}
impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>, state: Rc<RefCell<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));
Self {
inner,
state,
value,
}
}
}
impl NixList {
/// Forces the evaluation on all elements of the list.
///
fn as_vec(&self) -> Vec<Value> {
// 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<NixThunk> {
// 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));
<NixThunk as NixValue>::from(inner, self.state.clone())
}
}

View file

@ -10,21 +10,145 @@ mod path;
mod string; mod string;
mod thunk; mod thunk;
pub use attrs::NixAttrs;
pub use bool::NixBool; pub use bool::NixBool;
pub use external::NixExternal;
// pub use failed::NixFailed; // only in latest nix version
pub use float::NixFloat; pub use float::NixFloat;
pub use function::NixFunction;
pub use int::NixInt; pub use int::NixInt;
pub use list::NixList;
pub use null::NixNull;
pub use path::NixPath;
pub use string::NixString; pub use string::NixString;
pub use thunk::NixThunk;
use std::cell::RefCell;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::ptr::NonNull; use std::ptr::NonNull;
use std::rc::Rc;
use crate::EvalState;
use crate::errors::ErrorContext;
use crate::sys; 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::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap};
pub trait NixValue: Drop + Display + Debug + AsInnerPtr<sys::nix_value> { pub trait NixValue: Drop + Display + Debug + AsInnerPtr<sys::nix_value> {
/// TODO /// TODO
fn id(&self) -> sys::ValueType; fn type_id(&self) -> sys::ValueType;
/// TODO /// TODO
fn new(inner: NonNull<sys::nix_value>) -> Self; fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> 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<sys::nix_value>, Rc<RefCell<EvalState>>)> for Value {
fn from(value: (NonNull<sys::nix_value>, Rc<RefCell<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())
})
.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(<NixThunk as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_INT => Value::Int(<NixInt as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_FLOAT => Value::Float(<NixFloat as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_BOOL => Value::Bool(<NixBool as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_STRING => Value::String(<NixString as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_PATH => Value::Path(<NixPath as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_NULL => Value::Null(<NixNull as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_ATTRS => Value::Attrs(<NixAttrs as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_LIST => Value::List(<NixList as NixValue>::from(inner, state)),
ValueType_NIX_TYPE_FUNCTION => {
Value::Function(<NixFunction as NixValue>::from(inner, state))
},
ValueType_NIX_TYPE_EXTERNAL => {
Value::External(<NixExternal as NixValue>::from(inner, state))
},
// | sys::ValueType_NIX_TYPE_FAILED => {
// Value::Failed(<NixFailed as NixValue>::from(inner, state))
// },
_ => unreachable!(),
}
}
} }

View file

@ -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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
}
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<sys::nix_value> 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<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> Self {
Self { inner, state }
}
}

View file

@ -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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
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, "<path>")
}
}
impl Debug for NixPath {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "NixPath(\"${}\")", self.value().display())
}
}
impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> 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
}
}

View file

@ -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::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ptr::NonNull; use std::ptr::NonNull;
use std::rc::Rc;
use super::NixValue; use super::NixValue;
use crate::errors::ErrorContext; use crate::errors::ErrorContext;
use crate::expr::RealisedString;
use crate::util::panic_issue_call_failed; use crate::util::panic_issue_call_failed;
use crate::util::wrap; use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr; use crate::util::wrappers::AsInnerPtr;
use crate::{sys, NixideResult}; use crate::{EvalState, sys};
pub struct NixString { pub struct NixString {
inner: NonNull<sys::nix_value>, inner: NonNull<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: String, value: String,
} }
@ -55,23 +57,28 @@ impl AsInnerPtr<sys::nix_value> for NixString {
impl NixValue for NixString { impl NixValue for NixString {
#[inline] #[inline]
fn id(&self) -> sys::ValueType { fn type_id(&self) -> sys::ValueType {
sys::ValueType_NIX_TYPE_STRING sys::ValueType_NIX_TYPE_STRING
} }
fn new(inner: NonNull<sys::nix_value>) -> Self { fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> Self {
// wrap::nix_fn!(|ctx: &ErrorContext| unsafe { let value = wrap::nix_string_callback!(
// sys::nix_get_int(ctx.as_ptr(), inner.as_ptr()) |callback, userdata: *mut __UserData, ctx: &ErrorContext| unsafe {
// }) sys::nix_get_string(
// .unwrap_or_else(|err| { ctx.as_ptr(),
// panic_issue_call_failed!( inner.as_ptr(),
// "`sys::nix_get_int` failed for valid `NixString` ({})", Some(callback),
// err userdata as *mut c_void,
// ) );
// }) }
// }; )
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err));
Self { inner, value } Self {
inner,
state,
value,
}
} }
} }

View file

@ -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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
}
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, "<thunk>")
}
}
impl Debug for NixThunk {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "NixThunk")
}
}
impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> Self {
Self { inner, state }
}
}
impl NixThunk {
fn to_inner(self) -> NonNull<sys::nix_value> {
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()))
}
}

View file

@ -1,5 +1,7 @@
// #![warn(missing_docs)] // #![warn(missing_docs)]
#![cfg_attr(nightly, feature(fn_traits))]
pub extern crate libc; pub extern crate libc;
pub extern crate nixide_sys as sys; pub extern crate nixide_sys as sys;
@ -17,11 +19,11 @@ mod flake;
mod store; mod store;
pub use errors::{NixError, NixideError, NixideResult}; pub use errors::{NixError, NixideError, NixideResult};
pub use verbosity::{set_verbosity, NixVerbosity}; pub use verbosity::{NixVerbosity, set_verbosity};
pub use version::NixVersion; pub use version::NixVersion;
#[cfg(feature = "expr")] #[cfg(feature = "expr")]
pub use expr::{EvalState, EvalStateBuilder, Value, ValueType}; pub use expr::{EvalState, EvalStateBuilder, Value};
#[cfg(feature = "store")] #[cfg(feature = "store")]
pub use store::{Store, StorePath}; pub use store::{Store, StorePath};

View file

@ -9,10 +9,10 @@ macro_rules! panic_issue {
macro_rules! panic_issue_call_failed { 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),*) => {{ ($($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),*))
}}; }};
} }