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 {
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!"
),
}
}

View file

@ -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)))
}
}

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::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.
///

View file

@ -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::*;

View file

@ -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<sys::nix_value>, 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<String> {
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("{ <attrs> }".to_string()),
ValueType::List => Ok("[ <list> ]".to_string()),
ValueType::Function => Ok("<function>".to_string()),
ValueType::Path => Ok("<path>".to_string()),
ValueType::Thunk => Ok("<thunk>".to_string()),
ValueType::External => Ok("<external>".to_string()),
| ValueType::String => Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\""))),
| ValueType::Null => Ok("null".to_string()),
| ValueType::Attrs => Ok("{ <attrs> }".to_string()),
| ValueType::List => Ok("[ <list> ]".to_string()),
| ValueType::Function => Ok("<function>".to_string()),
| ValueType::Path => Ok("<path>".to_string()),
| ValueType::Thunk => Ok("<thunk>".to_string()),
| ValueType::External => Ok("<external>".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, "<int error>")
}
}
ValueType::Float => {
},
| ValueType::Float => {
if let Ok(val) = self.as_float() {
write!(f, "{val}")
} else {
write!(f, "<float error>")
}
}
ValueType::Bool => {
},
| ValueType::Bool => {
if let Ok(val) = self.as_bool() {
write!(f, "{val}")
} else {
write!(f, "<bool error>")
}
}
ValueType::String => {
},
| ValueType::String => {
if let Ok(val) = self.as_string() {
write!(f, "{val}")
} else {
write!(f, "<string error>")
}
}
ValueType::Null => write!(f, "null"),
ValueType::Attrs => write!(f, "{{ <attrs> }}"),
ValueType::List => write!(f, "[ <list> ]"),
ValueType::Function => write!(f, "<function>"),
ValueType::Path => write!(f, "<path>"),
ValueType::Thunk => write!(f, "<thunk>"),
ValueType::External => write!(f, "<external>"),
},
| ValueType::Null => write!(f, "null"),
| ValueType::Attrs => write!(f, "{{ <attrs> }}"),
| ValueType::List => write!(f, "[ <list> ]"),
| ValueType::Function => write!(f, "<function>"),
| ValueType::Path => write!(f, "<path>"),
| ValueType::Thunk => write!(f, "<thunk>"),
| ValueType::External => write!(f, "<external>"),
}
}
}
@ -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(<error>)")
}
}
ValueType::Float => {
},
| ValueType::Float => {
if let Ok(val) = self.as_float() {
write!(f, "Value::Float({val})")
} else {
write!(f, "Value::Float(<error>)")
}
}
ValueType::Bool => {
},
| ValueType::Bool => {
if let Ok(val) = self.as_bool() {
write!(f, "Value::Bool({val})")
} else {
write!(f, "Value::Bool(<error>)")
}
}
ValueType::String => {
},
| ValueType::String => {
if let Ok(val) = self.as_string() {
write!(f, "Value::String({val:?})")
} else {
write!(f, "Value::String(<error>)")
}
}
ValueType::Null => write!(f, "Value::Null"),
ValueType::Attrs => write!(f, "Value::Attrs({{ <attrs> }})"),
ValueType::List => write!(f, "Value::List([ <list> ])"),
ValueType::Function => write!(f, "Value::Function(<function>)"),
ValueType::Path => write!(f, "Value::Path(<path>)"),
ValueType::Thunk => write!(f, "Value::Thunk(<thunk>)"),
ValueType::External => write!(f, "Value::External(<external>)"),
},
| ValueType::Null => write!(f, "Value::Null"),
| ValueType::Attrs => write!(f, "Value::Attrs({{ <attrs> }})"),
| ValueType::List => write!(f, "Value::List([ <list> ])"),
| ValueType::Function => write!(f, "Value::Function(<function>)"),
| ValueType::Path => write!(f, "Value::Path(<path>)"),
| ValueType::Thunk => write!(f, "Value::Thunk(<thunk>)"),
| 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::ptr::NonNull;
use std::rc::Rc;
use super::NixValue;
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::panic_issue_call_failed;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr;
use crate::{EvalState, sys};
pub struct NixBool {
inner: NonNull<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: bool,
}
@ -53,11 +56,11 @@ impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> 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,
}
}
}

View file

@ -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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: f64,
}
@ -52,11 +55,11 @@ impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>) -> Self {
fn from(inner: NonNull<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> 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,
}
}
}

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::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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: i64,
}
@ -52,19 +55,21 @@ impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>) -> Self {
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!("`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,
}
}
}

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 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<sys::nix_value> {
/// TODO
fn id(&self) -> sys::ValueType;
fn type_id(&self) -> sys::ValueType;
/// 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::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<sys::nix_value>,
state: Rc<RefCell<EvalState>>,
value: String,
}
@ -55,23 +57,28 @@ impl AsInnerPtr<sys::nix_value> 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<sys::nix_value>) -> 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<sys::nix_value>, state: Rc<RefCell<EvalState>>) -> 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,
}
}
}

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)]
#![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};

View file

@ -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),*))
}};
}