maint: Move to /rust/* to /
This makes it easier for tooling to find the Rust stuff. Rust/non-rust is not a useful distinction in this repo anymore anyway.
This commit is contained in:
parent
8c64b5e380
commit
55eacf43c3
33 changed files with 5 additions and 5 deletions
2646
nix-bindings-expr/src/eval_state.rs
Normal file
2646
nix-bindings-expr/src/eval_state.rs
Normal file
File diff suppressed because it is too large
Load diff
3
nix-bindings-expr/src/lib.rs
Normal file
3
nix-bindings-expr/src/lib.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod eval_state;
|
||||
pub mod primop;
|
||||
pub mod value;
|
||||
119
nix-bindings-expr/src/primop.rs
Normal file
119
nix-bindings-expr/src/primop.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use crate::eval_state::{EvalState, EvalStateWeak};
|
||||
use crate::value::Value;
|
||||
use anyhow::Result;
|
||||
use nix_bindings_bindgen_raw as raw;
|
||||
use nix_bindings_util::check_call;
|
||||
use std::ffi::{c_int, c_void, CStr, CString};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ptr::{null, null_mut};
|
||||
|
||||
/// Metadata for a primop, used with `PrimOp::new`.
|
||||
pub struct PrimOpMeta<'a, const N: usize> {
|
||||
/// Name of the primop. Note that primops do not have to be registered as
|
||||
/// builtins. Nonetheless, a name is required for documentation purposes, e.g.
|
||||
/// :doc in the repl.
|
||||
pub name: &'a CStr,
|
||||
|
||||
/// Documentation for the primop. This is displayed in the repl when using
|
||||
/// :doc. The format is markdown.
|
||||
pub doc: &'a CStr,
|
||||
|
||||
/// The number of arguments the function takes, as well as names for the
|
||||
/// arguments, to be presented in the documentation (if applicable, e.g.
|
||||
/// :doc in the repl).
|
||||
pub args: [&'a CStr; N],
|
||||
}
|
||||
|
||||
pub struct PrimOp {
|
||||
pub(crate) ptr: *mut raw::PrimOp,
|
||||
}
|
||||
impl Drop for PrimOp {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
raw::gc_decref(null_mut(), self.ptr as *mut c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PrimOp {
|
||||
pub fn new<const N: usize>(
|
||||
eval_state: &mut EvalState,
|
||||
meta: PrimOpMeta<N>,
|
||||
f: Box<dyn Fn(&mut EvalState, &[Value; N]) -> Result<Value>>,
|
||||
) -> Result<PrimOp> {
|
||||
assert!(N != 0);
|
||||
|
||||
let mut args = Vec::new();
|
||||
for arg in meta.args {
|
||||
args.push(arg.as_ptr());
|
||||
}
|
||||
args.push(null());
|
||||
|
||||
// Primops weren't meant to be dynamically created, as of writing.
|
||||
// This leaks, and so do the primop fields in Nix internally.
|
||||
let user_data = {
|
||||
// We'll be leaking this Box.
|
||||
// TODO: Use the GC with finalizer, if possible.
|
||||
let user_data = ManuallyDrop::new(Box::new(PrimOpContext {
|
||||
arity: N,
|
||||
function: Box::new(move |eval_state, args| f(eval_state, args.try_into().unwrap())),
|
||||
eval_state: eval_state.weak_ref(),
|
||||
}));
|
||||
user_data.as_ref() as *const PrimOpContext as *mut c_void
|
||||
};
|
||||
let op = unsafe {
|
||||
check_call!(raw::alloc_primop(
|
||||
&mut eval_state.context,
|
||||
FUNCTION_ADAPTER,
|
||||
N as c_int,
|
||||
meta.name.as_ptr(),
|
||||
args.as_mut_ptr(), /* TODO add an extra const to bindings to avoid mut here. */
|
||||
meta.doc.as_ptr(),
|
||||
user_data
|
||||
))?
|
||||
};
|
||||
Ok(PrimOp { ptr: op })
|
||||
}
|
||||
}
|
||||
|
||||
/// The user_data for our Nix primops
|
||||
struct PrimOpContext {
|
||||
arity: usize,
|
||||
function: Box<dyn Fn(&mut EvalState, &[Value]) -> Result<Value>>,
|
||||
eval_state: EvalStateWeak,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn function_adapter(
|
||||
user_data: *mut ::std::os::raw::c_void,
|
||||
context_out: *mut raw::c_context,
|
||||
_state: *mut raw::EvalState,
|
||||
args: *mut *mut raw::Value,
|
||||
ret: *mut raw::Value,
|
||||
) {
|
||||
let primop_info = (user_data as *const PrimOpContext).as_ref().unwrap();
|
||||
let mut eval_state = primop_info.eval_state.upgrade().unwrap_or_else(|| {
|
||||
panic!("Nix primop called after EvalState was dropped");
|
||||
});
|
||||
let args_raw_slice = unsafe { std::slice::from_raw_parts(args, primop_info.arity) };
|
||||
let args_vec: Vec<Value> = args_raw_slice
|
||||
.iter()
|
||||
.map(|v| Value::new_borrowed(*v))
|
||||
.collect();
|
||||
let args_slice = args_vec.as_slice();
|
||||
|
||||
let r = primop_info.function.as_ref()(&mut eval_state, args_slice);
|
||||
|
||||
match r {
|
||||
Ok(v) => unsafe {
|
||||
raw::copy_value(context_out, ret, v.raw_ptr());
|
||||
},
|
||||
Err(e) => unsafe {
|
||||
let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| {
|
||||
CString::new("<rust nix-expr application error message contained null byte>")
|
||||
.unwrap()
|
||||
});
|
||||
raw::set_err_msg(context_out, raw::err_NIX_ERR_UNKNOWN, cstr.as_ptr());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter);
|
||||
126
nix-bindings-expr/src/value.rs
Normal file
126
nix-bindings-expr/src/value.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
pub mod __private;
|
||||
|
||||
use nix_bindings_bindgen_raw as raw;
|
||||
use nix_bindings_util::{check_call, context::Context};
|
||||
use std::ptr::{null_mut, NonNull};
|
||||
|
||||
// TODO: test: cloning a thunk does not duplicate the evaluation.
|
||||
|
||||
pub type Int = i64;
|
||||
|
||||
/// The type discriminator of a [`Value`] that has successfully evaluated to at least [weak head normal form](https://nix.dev/manual/nix/latest/language/evaluation.html?highlight=WHNF#values).
|
||||
///
|
||||
/// Typically acquired with [`EvalState::value_type`][`crate::eval_state::EvalState::value_type`]
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub enum ValueType {
|
||||
/// A Nix [attribute set](https://nix.dev/manual/nix/stable/language/types.html#type-attrs)
|
||||
AttrSet,
|
||||
/// A Nix [boolean](https://nix.dev/manual/nix/stable/language/types.html#type-bool)
|
||||
Bool,
|
||||
/// A Nix external value (mostly-opaque value for plugins, linked applications)
|
||||
External,
|
||||
/// A Nix [float](https://nix.dev/manual/nix/stable/language/types.html#type-float)
|
||||
Float,
|
||||
/// A Nix [function](https://nix.dev/manual/nix/stable/language/types.html#type-function)
|
||||
Function,
|
||||
/// A Nix [integer](https://nix.dev/manual/nix/stable/language/types.html#type-int)
|
||||
Int,
|
||||
/// A Nix [list](https://nix.dev/manual/nix/stable/language/types.html#type-list)
|
||||
List,
|
||||
/// A Nix [`null`](https://nix.dev/manual/nix/stable/language/types.html#type-null)
|
||||
Null,
|
||||
/// A Nix [path value](https://nix.dev/manual/nix/stable/language/types.html#type-path)
|
||||
Path,
|
||||
/// A Nix [string](https://nix.dev/manual/nix/stable/language/types.html#type-string)
|
||||
String,
|
||||
/// An unknown value, presumably from a new, partially unsupported version of Nix
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl ValueType {
|
||||
/// Convert a raw value type to a [`ValueType`].
|
||||
///
|
||||
/// Return [`None`] if the Value is still a thunk (i.e. not yet evaluated).
|
||||
///
|
||||
/// Return `Some(`[`ValueType::Unknown`]`)` if the value type is not recognized.
|
||||
pub(crate) fn from_raw(raw: raw::ValueType) -> Option<ValueType> {
|
||||
match raw {
|
||||
raw::ValueType_NIX_TYPE_ATTRS => Some(ValueType::AttrSet),
|
||||
raw::ValueType_NIX_TYPE_BOOL => Some(ValueType::Bool),
|
||||
raw::ValueType_NIX_TYPE_EXTERNAL => Some(ValueType::External),
|
||||
raw::ValueType_NIX_TYPE_FLOAT => Some(ValueType::Float),
|
||||
raw::ValueType_NIX_TYPE_FUNCTION => Some(ValueType::Function),
|
||||
raw::ValueType_NIX_TYPE_INT => Some(ValueType::Int),
|
||||
raw::ValueType_NIX_TYPE_LIST => Some(ValueType::List),
|
||||
raw::ValueType_NIX_TYPE_NULL => Some(ValueType::Null),
|
||||
raw::ValueType_NIX_TYPE_PATH => Some(ValueType::Path),
|
||||
raw::ValueType_NIX_TYPE_STRING => Some(ValueType::String),
|
||||
|
||||
raw::ValueType_NIX_TYPE_THUNK => None,
|
||||
|
||||
// This would happen if a new type of value is added in Nix.
|
||||
_ => Some(ValueType::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A pointer to a [value](https://nix.dev/manual/nix/latest/language/types.html) or [thunk](https://nix.dev/manual/nix/2.31/language/evaluation.html?highlight=thunk#laziness), to be used with [`EvalState`][`crate::eval_state::EvalState`] methods.
|
||||
pub struct Value {
|
||||
inner: NonNull<raw::Value>,
|
||||
}
|
||||
impl Value {
|
||||
/// Take ownership of a new [`Value`].
|
||||
///
|
||||
/// This does not call [`nix_c_raw::gc_incref`], but does call [`nix_c_raw::nix_gc_decref`] when [dropped][`Drop`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the provided `inner` has a positive reference count, and that `inner` is not used after the returned `Value` is dropped.
|
||||
pub(crate) unsafe fn new(inner: *mut raw::Value) -> Self {
|
||||
Value {
|
||||
inner: NonNull::new(inner).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow a reference to a [`Value`].
|
||||
///
|
||||
/// This calls [`nix_c_raw::value_incref`], and the returned Value will call [`nix_c_raw::value_decref`] when dropped.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the provided `inner` has a positive reference count.
|
||||
pub(crate) unsafe fn new_borrowed(inner: *mut raw::Value) -> Self {
|
||||
let v = Value::new(inner);
|
||||
unsafe { raw::value_incref(null_mut(), inner) };
|
||||
v
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the returned pointer is not used after the `Value` is dropped.
|
||||
pub(crate) unsafe fn raw_ptr(&self) -> *mut raw::Value {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
}
|
||||
impl Drop for Value {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// ignoring error because the only failure mode is leaking memory
|
||||
raw::value_decref(null_mut(), self.inner.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Clone for Value {
|
||||
fn clone(&self) -> Self {
|
||||
// TODO: Is it worth allocating a new Context here? Ideally cloning is cheap.
|
||||
// this is very unlikely to error, and it is not recoverable
|
||||
// Maybe try without, and try again with context to report details?
|
||||
unsafe {
|
||||
check_call!(raw::value_incref(&mut Context::new(), self.inner.as_ptr())).unwrap();
|
||||
}
|
||||
// can't return an error here, but we don't want to ignore the error either as it means we could use-after-free
|
||||
Value { inner: self.inner }
|
||||
}
|
||||
}
|
||||
|
||||
// Tested in eval_state.rs
|
||||
13
nix-bindings-expr/src/value/__private.rs
Normal file
13
nix-bindings-expr/src/value/__private.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//! Functions that are relevant for other bindings modules, but normally not end users.
|
||||
use super::Value;
|
||||
use nix_bindings_bindgen_raw as raw;
|
||||
|
||||
/// See [Value::new].
|
||||
pub unsafe fn raw_value_new(ptr: *mut raw::Value) -> Value {
|
||||
Value::new(ptr)
|
||||
}
|
||||
|
||||
/// See [Value::new_borrowed].
|
||||
pub unsafe fn raw_value_new_borrowed(ptr: *mut raw::Value) -> Value {
|
||||
Value::new_borrowed(ptr)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue