idk, like a bunch
This commit is contained in:
parent
f8c1625d5c
commit
88b575b8ca
21 changed files with 1013 additions and 128 deletions
|
|
@ -1,17 +1,56 @@
|
||||||
use std::ptr::NonNull;
|
use std::ffi::{c_char, c_uint, CStr, CString};
|
||||||
|
use std::ptr::{null_mut, NonNull};
|
||||||
|
|
||||||
use crate::error::NixError;
|
use crate::error::NixErrorCode;
|
||||||
use nixide_sys as sys;
|
use crate::sys;
|
||||||
|
use crate::util::bindings::wrap_libnix_string_callback;
|
||||||
|
|
||||||
/// Nix context for managing library state.
|
// XXX: TODO: change this to a `Result<T, NixError>`
|
||||||
|
type NixResult<T> = Result<T, NixErrorCode>;
|
||||||
|
|
||||||
|
pub struct NixError {
|
||||||
|
pub code: NixErrorCode,
|
||||||
|
pub name: String,
|
||||||
|
pub msg: Option<String>,
|
||||||
|
pub info_msg: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This object stores error state.
|
||||||
///
|
///
|
||||||
/// This is the root object for all Nix operations. It manages the lifetime
|
/// Passed as a first parameter to functions that can fail, to store error
|
||||||
/// of the Nix C API context and provides automatic cleanup.
|
/// information.
|
||||||
pub struct Context {
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// These can be reused between different function calls,
|
||||||
|
/// but make sure not to use them for multiple calls simultaneously
|
||||||
|
/// (which can happen in callbacks).
|
||||||
|
///
|
||||||
|
/// # `libnix` API Internals
|
||||||
|
///
|
||||||
|
/// ```cpp
|
||||||
|
/// struct nix_c_context
|
||||||
|
/// {
|
||||||
|
/// nix_err last_err_code = NIX_OK;
|
||||||
|
/// /* WARNING: The last error message. Always check last_err_code.
|
||||||
|
/// WARNING: This may not have been cleared, so that clearing is fast. */
|
||||||
|
/// std::optional<std::string> last_err = {};
|
||||||
|
/// std::optional<nix::ErrorInfo> info = {};
|
||||||
|
/// std::string name = "";
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The [sys::nix_c_context] struct is laid out so that it can also be
|
||||||
|
/// cast to a [sys::nix_err] to inspect directly:
|
||||||
|
/// ```c
|
||||||
|
/// assert(*(nix_err*)ctx == NIX_OK);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub struct ErrorContext {
|
||||||
inner: NonNull<sys::nix_c_context>,
|
inner: NonNull<sys::nix_c_context>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl ErrorContext {
|
||||||
/// Create a new Nix context.
|
/// Create a new Nix context.
|
||||||
///
|
///
|
||||||
/// This initializes the Nix C API context and the required libraries.
|
/// This initializes the Nix C API context and the required libraries.
|
||||||
|
|
@ -19,30 +58,31 @@ impl Context {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if context creation or library initialization fails.
|
/// Returns an error if context creation or library initialization fails.
|
||||||
pub fn new() -> Result<Self, NixError> {
|
pub fn new() -> Result<Self, NixErrorCode> {
|
||||||
// SAFETY: nix_c_context_create is safe to call
|
// SAFETY: nix_c_context_create is safe to call
|
||||||
let ctx_ptr = unsafe { sys::nix_c_context_create() };
|
let ctx_ptr = unsafe { sys::nix_c_context_create() };
|
||||||
let ctx = Context {
|
let ctx = ErrorContext {
|
||||||
inner: NonNull::new(ctx_ptr).ok_or(NixError::NullPtr {
|
inner: NonNull::new(ctx_ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
location: "nix_c_context_create",
|
location: "nix_c_context_create",
|
||||||
})?,
|
})?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize required libraries
|
// Initialize required libraries
|
||||||
unsafe {
|
// XXX: TODO: move this to a separate init function (maybe a Nix::init() function)
|
||||||
NixError::from(
|
// unsafe {
|
||||||
sys::nix_libutil_init(ctx.inner.as_ptr()),
|
// NixErrorCode::from(
|
||||||
"nix_libutil_init",
|
// sys::nix_libutil_init(ctx.inner.as_ptr()),
|
||||||
)?;
|
// "nix_libutil_init",
|
||||||
NixError::from(
|
// )?;
|
||||||
sys::nix_libstore_init(ctx.inner.as_ptr()),
|
// NixErrorCode::from(
|
||||||
"nix_libstore_init",
|
// sys::nix_libstore_init(ctx.inner.as_ptr()),
|
||||||
)?;
|
// "nix_libstore_init",
|
||||||
NixError::from(
|
// )?;
|
||||||
sys::nix_libexpr_init(ctx.inner.as_ptr()),
|
// NixErrorCode::from(
|
||||||
"nix_libexpr_init",
|
// sys::nix_libexpr_init(ctx.inner.as_ptr()),
|
||||||
)?;
|
// "nix_libexpr_init",
|
||||||
};
|
// )?;
|
||||||
|
// };
|
||||||
|
|
||||||
Ok(ctx)
|
Ok(ctx)
|
||||||
}
|
}
|
||||||
|
|
@ -51,13 +91,109 @@ impl Context {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure the pointer is used safely.
|
/// Although this function isn't inherently `unsafe`, it is
|
||||||
pub unsafe fn as_ptr(&self) -> *mut sys::nix_c_context {
|
/// marked as such intentionally to force calls to be wrapped
|
||||||
|
/// in `unsafe` blocks for clarity.
|
||||||
|
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_c_context {
|
||||||
self.inner.as_ptr()
|
self.inner.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check the error code and return an error if it's not `NIX_OK`.
|
||||||
|
///
|
||||||
|
/// We recommend to use `check_call!` if possible.
|
||||||
|
pub fn peak(&self) -> Result<(), NixErrorCode> {
|
||||||
|
// NixError::from( unsafe { sys::nix_err_code(self.as_ptr())}, "");
|
||||||
|
|
||||||
|
let err = unsafe { sys::nix_err_code(self.inner.as_ptr()) };
|
||||||
|
if err != sys::nix_err_NIX_OK {
|
||||||
|
// msgp is a borrowed pointer (pointing into the context), so we don't need to free it
|
||||||
|
let msgp = unsafe { sys::nix_err_msg(null_mut(), self.inner.as_ptr(), null_mut()) };
|
||||||
|
// Turn the i8 pointer into a Rust string by copying
|
||||||
|
let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? };
|
||||||
|
bail!("{}", msg);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Result<(), NixErrorCode> {
|
||||||
|
let result = self.peak();
|
||||||
|
if result.is_err() {
|
||||||
|
self.clear();
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// NOTE: previous nixops4 used the line: (maybe for compatability with old versions?)
|
||||||
|
// sys::nix_set_err_msg(self.inner.as_ptr(), sys::nix_err_NIX_OK, c"".as_ptr());
|
||||||
|
sys::nix_clear_err(self.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Never fails
|
||||||
|
pub(crate) fn get_code(&self) -> Result<(), NixErrorCode> {
|
||||||
|
NixErrorCode::from(unsafe { sys::nix_err_code(self.as_ptr()) }, "nix_err_code")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_name(&self, result: NixResult<()>) -> Option<String> {
|
||||||
|
match result {
|
||||||
|
Err(code) => unsafe {
|
||||||
|
let ctx = null_mut();
|
||||||
|
wrap_libnix_string_callback("nix_err_name", |callback, user_data| {
|
||||||
|
sys::nix_err_name(ctx, self.as_ptr(), Some(callback), user_data)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Ok(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Note
|
||||||
|
/// On failure [sys::nix_err_name] does the following if the error
|
||||||
|
/// has the error code [sys::nix_err_NIX_OK]:
|
||||||
|
/// ```c
|
||||||
|
/// nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message");
|
||||||
|
/// return nullptr;
|
||||||
|
/// ```
|
||||||
|
/// Hence we can just test whether the returned pointer is a `NULL` pointer,
|
||||||
|
/// and avoid passing in a [sys::nix_c_context] struct.
|
||||||
|
pub(crate) fn get_msg(&self, result: NixResult<()>) -> Option<String> {
|
||||||
|
match result {
|
||||||
|
Err(_) => unsafe {
|
||||||
|
let ctx = null_mut();
|
||||||
|
let msg_ptr: *const c_char = sys::nix_err_msg(ctx, self.as_ptr(), null_mut());
|
||||||
|
|
||||||
|
if msg_ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match CStr::from_ptr(msg_ptr).to_str() {
|
||||||
|
Ok(msg_str) => Some(msg_str.to_string()),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_info_msg(&self) -> Option<String> {}
|
||||||
|
|
||||||
|
pub fn check_one_call_or_key_none<T, F>(&mut self, f: F) -> Result<Option<T>, NixErrorCode>
|
||||||
|
where
|
||||||
|
F: FnOnce(*mut sys::nix_c_context) -> T,
|
||||||
|
{
|
||||||
|
let t = f(unsafe { self.as_ptr() });
|
||||||
|
if unsafe { sys::nix_err_code(self.inner.as_ptr()) == sys::nix_err_NIX_ERR_KEY } {
|
||||||
|
self.clear();
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
self.pop()?;
|
||||||
|
Ok(Some(t))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Context {
|
impl Drop for ErrorContext {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// SAFETY: We own the context and it's valid until drop
|
// SAFETY: We own the context and it's valid until drop
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -65,7 +201,3 @@ impl Drop for Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: Context can be shared between threads
|
|
||||||
unsafe impl Send for Context {}
|
|
||||||
unsafe impl Sync for Context {}
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::sys;
|
||||||
/// Standard (nix_err) and some additional error codes
|
/// Standard (nix_err) and some additional error codes
|
||||||
/// produced by the libnix C API.
|
/// produced by the libnix C API.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NixError {
|
pub enum NixErrorCode {
|
||||||
/// A generic Nix error occurred.
|
/// A generic Nix error occurred.
|
||||||
///
|
///
|
||||||
/// # Reason
|
/// # Reason
|
||||||
|
|
@ -95,24 +95,24 @@ pub enum NixError {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NixError {
|
impl NixErrorCode {
|
||||||
pub fn from(err_code: sys::nix_err, location: &'static str) -> Result<(), NixError> {
|
pub fn from(err_code: sys::nix_err, location: &'static str) -> Result<(), NixErrorCode> {
|
||||||
#[allow(nonstandard_style)]
|
#[allow(nonstandard_style)]
|
||||||
match err_code {
|
match err_code {
|
||||||
sys::nix_err_NIX_OK => Ok(()),
|
sys::nix_err_NIX_OK => Ok(()),
|
||||||
|
|
||||||
sys::nix_err_NIX_ERR_OVERFLOW => Err(NixError::Overflow { location }),
|
sys::nix_err_NIX_ERR_OVERFLOW => Err(NixErrorCode::Overflow { location }),
|
||||||
sys::nix_err_NIX_ERR_KEY => Err(NixError::KeyNotFound {
|
sys::nix_err_NIX_ERR_KEY => Err(NixErrorCode::KeyNotFound {
|
||||||
location,
|
location,
|
||||||
key: None,
|
key: None,
|
||||||
}),
|
}),
|
||||||
sys::nix_err_NIX_ERR_NIX_ERROR => Err(NixError::NixError { location }),
|
sys::nix_err_NIX_ERR_NIX_ERROR => Err(NixErrorCode::NixError { location }),
|
||||||
|
|
||||||
sys::nix_err_NIX_ERR_UNKNOWN => Err(NixError::Unknown {
|
sys::nix_err_NIX_ERR_UNKNOWN => Err(NixErrorCode::Unknown {
|
||||||
location,
|
location,
|
||||||
reason: "Unknown error occurred".to_string(),
|
reason: "Unknown error occurred".to_string(),
|
||||||
}),
|
}),
|
||||||
_ => Err(NixError::Undocumented { location, err_code }),
|
_ => Err(NixErrorCode::Undocumented { location, err_code }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,29 +120,29 @@ impl NixError {
|
||||||
result: Result<T, NulError>,
|
result: Result<T, NulError>,
|
||||||
location: &'static str,
|
location: &'static str,
|
||||||
) -> Result<T, Self> {
|
) -> Result<T, Self> {
|
||||||
result.or(Err(NixError::NulError { location }))
|
result.or(Err(NixErrorCode::NulError { location }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_nonnull<T>(ptr: *mut T, location: &'static str) -> Result<NonNull<T>, Self>
|
pub fn new_nonnull<T>(ptr: *mut T, location: &'static str) -> Result<NonNull<T>, Self>
|
||||||
where
|
where
|
||||||
T: Sized,
|
T: Sized,
|
||||||
{
|
{
|
||||||
NonNull::new(ptr).ok_or(NixError::NullPtr { location })
|
NonNull::new(ptr).ok_or(NixErrorCode::NullPtr { location })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for NixError {}
|
impl std::error::Error for NixErrorCode {}
|
||||||
|
|
||||||
impl Display for NixError {
|
impl Display for NixErrorCode {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
let msg = match self {
|
let msg = match self {
|
||||||
NixError::NixError { location } => {
|
NixErrorCode::NixError { location } => {
|
||||||
format!("[libnix] Generic error (at location `{location}`)")
|
format!("[libnix] Generic error (at location `{location}`)")
|
||||||
}
|
}
|
||||||
NixError::Overflow { location } => {
|
NixErrorCode::Overflow { location } => {
|
||||||
format!("[libnix] Overflow error (at location `{location}`)")
|
format!("[libnix] Overflow error (at location `{location}`)")
|
||||||
}
|
}
|
||||||
NixError::KeyNotFound { location, key } => format!(
|
NixErrorCode::KeyNotFound { location, key } => format!(
|
||||||
"[libnix] Key not found {} (at location `{location}`)",
|
"[libnix] Key not found {} (at location `{location}`)",
|
||||||
match key {
|
match key {
|
||||||
Some(key) => format!("`{key}`"),
|
Some(key) => format!("`{key}`"),
|
||||||
|
|
@ -150,26 +150,26 @@ impl Display for NixError {
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
NixError::Unknown { location, reason } => {
|
NixErrorCode::Unknown { location, reason } => {
|
||||||
format!("Unknown error \"{reason}\" (at location `{location}`)")
|
format!("Unknown error \"{reason}\" (at location `{location}`)")
|
||||||
}
|
}
|
||||||
NixError::Undocumented { location, err_code } => {
|
NixErrorCode::Undocumented { location, err_code } => {
|
||||||
format!(
|
format!(
|
||||||
"[libnix] An undocumented nix_err was returned with {err_code} (at location `{location}`)"
|
"[libnix] An undocumented nix_err was returned with {err_code} (at location `{location}`)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
NixError::NulError { location } => {
|
NixErrorCode::NulError { location } => {
|
||||||
format!("Nul error (at location `{location}`)")
|
format!("Nul error (at location `{location}`)")
|
||||||
}
|
}
|
||||||
NixError::NullPtr { location } => {
|
NixErrorCode::NullPtr { location } => {
|
||||||
format!("[libnix] Null pointer (at location `{location}`)")
|
format!("[libnix] Null pointer (at location `{location}`)")
|
||||||
}
|
}
|
||||||
|
|
||||||
NixError::InvalidArg { location, reason } => {
|
NixErrorCode::InvalidArg { location, reason } => {
|
||||||
format!("Invalid argument \"{reason}\" (at location `{location}`)")
|
format!("Invalid argument \"{reason}\" (at location `{location}`)")
|
||||||
}
|
}
|
||||||
NixError::InvalidType {
|
NixErrorCode::InvalidType {
|
||||||
location,
|
location,
|
||||||
expected,
|
expected,
|
||||||
got,
|
got,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
use crate::sys;
|
use crate::sys;
|
||||||
use crate::{Context, NixError, Store};
|
use crate::{ErrorContext, NixErrorCode, Store};
|
||||||
|
|
||||||
/// Nix evaluation state for evaluating expressions.
|
/// Nix evaluation state for evaluating expressions.
|
||||||
///
|
///
|
||||||
|
|
@ -14,7 +14,7 @@ pub struct EvalState {
|
||||||
inner: NonNull<sys::EvalState>,
|
inner: NonNull<sys::EvalState>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
store: Arc<Store>,
|
store: Arc<Store>,
|
||||||
pub(super) context: Arc<Context>,
|
pub(super) context: Arc<ErrorContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvalState {
|
impl EvalState {
|
||||||
|
|
@ -22,7 +22,7 @@ impl EvalState {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
inner: NonNull<sys::EvalState>,
|
inner: NonNull<sys::EvalState>,
|
||||||
store: Arc<Store>,
|
store: Arc<Store>,
|
||||||
context: Arc<Context>,
|
context: Arc<ErrorContext>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner,
|
inner,
|
||||||
|
|
@ -41,24 +41,24 @@ impl EvalState {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if evaluation fails.
|
/// Returns an error if evaluation fails.
|
||||||
pub fn eval_from_string(&self, expr: &str, path: &str) -> Result<Value<'_>, NixError> {
|
pub fn eval_from_string(&self, expr: &str, path: &str) -> Result<Value<'_>, NixErrorCode> {
|
||||||
let expr_c =
|
let expr_c =
|
||||||
NixError::from_nulerror(CString::new(expr), "nixide::EvalState::eval_from_string")?;
|
NixErrorCode::from_nulerror(CString::new(expr), "nixide::EvalState::eval_from_string")?;
|
||||||
let path_c =
|
let path_c =
|
||||||
NixError::from_nulerror(CString::new(path), "nixide::EvalState::eval_from_string")?;
|
NixErrorCode::from_nulerror(CString::new(path), "nixide::EvalState::eval_from_string")?;
|
||||||
|
|
||||||
// Allocate value for result
|
// Allocate value for result
|
||||||
// SAFETY: context and state are valid
|
// SAFETY: context and state are valid
|
||||||
let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) };
|
let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) };
|
||||||
if value_ptr.is_null() {
|
if value_ptr.is_null() {
|
||||||
return Err(NixError::NullPtr {
|
return Err(NixErrorCode::NullPtr {
|
||||||
location: "nix_alloc_value",
|
location: "nix_alloc_value",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate expression
|
// Evaluate expression
|
||||||
// SAFETY: all pointers are valid
|
// SAFETY: all pointers are valid
|
||||||
NixError::from(
|
NixErrorCode::from(
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::nix_expr_eval_from_string(
|
sys::nix_expr_eval_from_string(
|
||||||
self.context.as_ptr(),
|
self.context.as_ptr(),
|
||||||
|
|
@ -71,7 +71,7 @@ impl EvalState {
|
||||||
"nix_expr_eval_from_string",
|
"nix_expr_eval_from_string",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let inner = NonNull::new(value_ptr).ok_or(NixError::NullPtr {
|
let inner = NonNull::new(value_ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
location: "nix_expr_eval_from_string",
|
location: "nix_expr_eval_from_string",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -83,10 +83,10 @@ impl EvalState {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if value allocation fails.
|
/// Returns an error if value allocation fails.
|
||||||
pub fn alloc_value(&self) -> Result<Value<'_>, NixError> {
|
pub fn alloc_value(&self) -> Result<Value<'_>, NixErrorCode> {
|
||||||
// SAFETY: context and state are valid
|
// SAFETY: context and state are valid
|
||||||
let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) };
|
let value_ptr = unsafe { sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr()) };
|
||||||
let inner = NonNull::new(value_ptr).ok_or(NixError::NullPtr {
|
let inner = NonNull::new(value_ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
location: "nix_alloc_value",
|
location: "nix_alloc_value",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use super::EvalState;
|
use super::EvalState;
|
||||||
use crate::sys;
|
use crate::sys;
|
||||||
use crate::{Context, NixError, Store};
|
use crate::{ErrorContext, NixErrorCode, Store};
|
||||||
|
|
||||||
/// Builder for Nix evaluation state.
|
/// Builder for Nix evaluation state.
|
||||||
///
|
///
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::{Context, NixError, Store};
|
||||||
pub struct EvalStateBuilder {
|
pub struct EvalStateBuilder {
|
||||||
inner: NonNull<sys::nix_eval_state_builder>,
|
inner: NonNull<sys::nix_eval_state_builder>,
|
||||||
store: Arc<Store>,
|
store: Arc<Store>,
|
||||||
context: Arc<Context>,
|
context: Arc<ErrorContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvalStateBuilder {
|
impl EvalStateBuilder {
|
||||||
|
|
@ -25,12 +25,12 @@ impl EvalStateBuilder {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the builder cannot be created.
|
/// Returns an error if the builder cannot be created.
|
||||||
pub fn new(store: &Arc<Store>) -> Result<Self, NixError> {
|
pub fn new(store: &Arc<Store>) -> Result<Self, NixErrorCode> {
|
||||||
// SAFETY: store context and store are valid
|
// SAFETY: store context and store are valid
|
||||||
let builder_ptr =
|
let builder_ptr =
|
||||||
unsafe { sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr()) };
|
unsafe { sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr()) };
|
||||||
|
|
||||||
let inner = NonNull::new(builder_ptr).ok_or(NixError::NullPtr {
|
let inner = NonNull::new(builder_ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
location: "nix_eval_state_builder_new",
|
location: "nix_eval_state_builder_new",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -46,10 +46,10 @@ impl EvalStateBuilder {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the evaluation state cannot be built.
|
/// Returns an error if the evaluation state cannot be built.
|
||||||
pub fn build(self) -> Result<EvalState, NixError> {
|
pub fn build(self) -> Result<EvalState, NixErrorCode> {
|
||||||
// Load configuration first
|
// Load configuration first
|
||||||
// SAFETY: context and builder are valid
|
// SAFETY: context and builder are valid
|
||||||
NixError::from(
|
NixErrorCode::from(
|
||||||
unsafe { sys::nix_eval_state_builder_load(self.context.as_ptr(), self.inner.as_ptr()) },
|
unsafe { sys::nix_eval_state_builder_load(self.context.as_ptr(), self.inner.as_ptr()) },
|
||||||
"nix_eval_state_builder_load",
|
"nix_eval_state_builder_load",
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -59,7 +59,7 @@ impl EvalStateBuilder {
|
||||||
let state_ptr =
|
let state_ptr =
|
||||||
unsafe { sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr()) };
|
unsafe { sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr()) };
|
||||||
|
|
||||||
let inner = NonNull::new(state_ptr).ok_or(NixError::NullPtr {
|
let inner = NonNull::new(state_ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
location: "nix_eval_state_build",
|
location: "nix_eval_state_build",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -70,6 +70,10 @@ impl EvalStateBuilder {
|
||||||
self.context.clone(),
|
self.context.clone(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_eval_state_builder {
|
||||||
|
self.inner.as_ptr()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for EvalStateBuilder {
|
impl Drop for EvalStateBuilder {
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,19 @@ use std::sync::Arc;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
use super::{EvalStateBuilder, ValueType};
|
use super::{EvalStateBuilder, ValueType};
|
||||||
use crate::{Context, Store};
|
use crate::{ErrorContext, Store};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_context_creation() {
|
fn test_context_creation() {
|
||||||
let _ctx = Context::new().expect("Failed to create context");
|
let _ctx = ErrorContext::new().expect("Failed to create context");
|
||||||
// Context should be dropped automatically
|
// Context should be dropped automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_eval_state_builder() {
|
fn test_eval_state_builder() {
|
||||||
let ctx = Arc::new(Context::new().expect("Failed to create context"));
|
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
||||||
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
||||||
let _state = EvalStateBuilder::new(&store)
|
let _state = EvalStateBuilder::new(&store)
|
||||||
.expect("Failed to create builder")
|
.expect("Failed to create builder")
|
||||||
|
|
@ -27,7 +27,7 @@ fn test_eval_state_builder() {
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_simple_evaluation() {
|
fn test_simple_evaluation() {
|
||||||
let ctx = Arc::new(Context::new().expect("Failed to create context"));
|
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
||||||
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
||||||
let state = EvalStateBuilder::new(&store)
|
let state = EvalStateBuilder::new(&store)
|
||||||
.expect("Failed to create builder")
|
.expect("Failed to create builder")
|
||||||
|
|
@ -45,7 +45,7 @@ fn test_simple_evaluation() {
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_value_types() {
|
fn test_value_types() {
|
||||||
let ctx = Arc::new(Context::new().expect("Failed to create context"));
|
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
||||||
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
||||||
let state = EvalStateBuilder::new(&store)
|
let state = EvalStateBuilder::new(&store)
|
||||||
.expect("Failed to create builder")
|
.expect("Failed to create builder")
|
||||||
|
|
@ -77,7 +77,7 @@ fn test_value_types() {
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_value_formatting() {
|
fn test_value_formatting() {
|
||||||
let ctx = Arc::new(Context::new().expect("Failed to create context"));
|
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
||||||
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
let store = Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
|
||||||
let state = EvalStateBuilder::new(&store)
|
let state = EvalStateBuilder::new(&store)
|
||||||
.expect("Failed to create builder")
|
.expect("Failed to create builder")
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::ptr::NonNull;
|
||||||
|
|
||||||
use super::{EvalState, ValueType};
|
use super::{EvalState, ValueType};
|
||||||
use crate::sys;
|
use crate::sys;
|
||||||
use crate::NixError;
|
use crate::NixErrorCode;
|
||||||
|
|
||||||
/// A Nix value
|
/// A Nix value
|
||||||
///
|
///
|
||||||
|
|
@ -22,8 +22,8 @@ impl Value<'_> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if evaluation fails.
|
/// Returns an error if evaluation fails.
|
||||||
pub fn force(&mut self) -> Result<(), NixError> {
|
pub fn force(&mut self) -> Result<(), NixErrorCode> {
|
||||||
NixError::from(
|
NixErrorCode::from(
|
||||||
// SAFETY: context, state, and value are valid
|
// SAFETY: context, state, and value are valid
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::nix_value_force(
|
sys::nix_value_force(
|
||||||
|
|
@ -43,8 +43,8 @@ impl Value<'_> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if evaluation fails.
|
/// Returns an error if evaluation fails.
|
||||||
pub fn force_deep(&mut self) -> Result<(), NixError> {
|
pub fn force_deep(&mut self) -> Result<(), NixErrorCode> {
|
||||||
NixError::from(
|
NixErrorCode::from(
|
||||||
// SAFETY: context, state, and value are valid
|
// SAFETY: context, state, and value are valid
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::nix_value_force_deep(
|
sys::nix_value_force_deep(
|
||||||
|
|
@ -70,9 +70,9 @@ impl Value<'_> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the value is not an integer.
|
/// Returns an error if the value is not an integer.
|
||||||
pub fn as_int(&self) -> Result<i64, NixError> {
|
pub fn as_int(&self) -> Result<i64, NixErrorCode> {
|
||||||
if self.value_type() != ValueType::Int {
|
if self.value_type() != ValueType::Int {
|
||||||
return Err(NixError::InvalidType {
|
return Err(NixErrorCode::InvalidType {
|
||||||
location: "nixide::Value::as_int",
|
location: "nixide::Value::as_int",
|
||||||
expected: "int",
|
expected: "int",
|
||||||
got: self.value_type().to_string(),
|
got: self.value_type().to_string(),
|
||||||
|
|
@ -90,9 +90,9 @@ impl Value<'_> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the value is not a float.
|
/// Returns an error if the value is not a float.
|
||||||
pub fn as_float(&self) -> Result<f64, NixError> {
|
pub fn as_float(&self) -> Result<f64, NixErrorCode> {
|
||||||
if self.value_type() != ValueType::Float {
|
if self.value_type() != ValueType::Float {
|
||||||
return Err(NixError::InvalidType {
|
return Err(NixErrorCode::InvalidType {
|
||||||
location: "nixide::Value::as_float",
|
location: "nixide::Value::as_float",
|
||||||
expected: "float",
|
expected: "float",
|
||||||
got: self.value_type().to_string(),
|
got: self.value_type().to_string(),
|
||||||
|
|
@ -111,9 +111,9 @@ impl Value<'_> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the value is not a boolean.
|
/// Returns an error if the value is not a boolean.
|
||||||
pub fn as_bool(&self) -> Result<bool, NixError> {
|
pub fn as_bool(&self) -> Result<bool, NixErrorCode> {
|
||||||
if self.value_type() != ValueType::Bool {
|
if self.value_type() != ValueType::Bool {
|
||||||
return Err(NixError::InvalidType {
|
return Err(NixErrorCode::InvalidType {
|
||||||
location: "nixide::Value::as_bool",
|
location: "nixide::Value::as_bool",
|
||||||
expected: "bool",
|
expected: "bool",
|
||||||
got: self.value_type().to_string(),
|
got: self.value_type().to_string(),
|
||||||
|
|
@ -131,9 +131,9 @@ impl Value<'_> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the value is not a string.
|
/// Returns an error if the value is not a string.
|
||||||
pub fn as_string(&self) -> Result<String, NixError> {
|
pub fn as_string(&self) -> Result<String, NixErrorCode> {
|
||||||
if self.value_type() != ValueType::String {
|
if self.value_type() != ValueType::String {
|
||||||
return Err(NixError::InvalidType {
|
return Err(NixErrorCode::InvalidType {
|
||||||
location: "nixide::Value::as_string",
|
location: "nixide::Value::as_string",
|
||||||
expected: "string",
|
expected: "string",
|
||||||
got: self.value_type().to_string(),
|
got: self.value_type().to_string(),
|
||||||
|
|
@ -152,7 +152,7 @@ impl Value<'_> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if realised_str.is_null() {
|
if realised_str.is_null() {
|
||||||
return Err(NixError::NullPtr {
|
return Err(NixErrorCode::NullPtr {
|
||||||
location: "nix_string_realise",
|
location: "nix_string_realise",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +165,7 @@ impl Value<'_> {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::nix_realised_string_free(realised_str);
|
sys::nix_realised_string_free(realised_str);
|
||||||
}
|
}
|
||||||
return Err(NixError::NullPtr {
|
return Err(NixErrorCode::NullPtr {
|
||||||
location: "nix_realised_string_free",
|
location: "nix_realised_string_free",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +173,7 @@ impl Value<'_> {
|
||||||
// SAFETY: buffer_start is non-null and buffer_size gives us the length
|
// SAFETY: buffer_start is non-null and buffer_size gives us the length
|
||||||
let bytes = unsafe { std::slice::from_raw_parts(buffer_start.cast::<u8>(), buffer_size) };
|
let bytes = unsafe { std::slice::from_raw_parts(buffer_start.cast::<u8>(), buffer_size) };
|
||||||
let string = std::str::from_utf8(bytes)
|
let string = std::str::from_utf8(bytes)
|
||||||
.map_err(|_| NixError::Unknown {
|
.map_err(|_| NixErrorCode::Unknown {
|
||||||
location: "nixide::Value::as_string",
|
location: "nixide::Value::as_string",
|
||||||
reason: "Invalid UTF-8 in string".to_string(),
|
reason: "Invalid UTF-8 in string".to_string(),
|
||||||
})?
|
})?
|
||||||
|
|
@ -206,7 +206,7 @@ impl Value<'_> {
|
||||||
///
|
///
|
||||||
/// Returns an error if the value cannot be converted to a string
|
/// Returns an error if the value cannot be converted to a string
|
||||||
/// representation.
|
/// representation.
|
||||||
pub fn to_nix_string(&self) -> Result<String, NixError> {
|
pub fn to_nix_string(&self) -> Result<String, NixErrorCode> {
|
||||||
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()),
|
||||||
|
|
|
||||||
17
nixide/src/flake/eval_state_builder_ext.rs
Normal file
17
nixide/src/flake/eval_state_builder_ext.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
pub trait EvalStateBuilderExt {
|
||||||
|
/// Configures the eval state to provide flakes features such as `builtins.getFlake`.
|
||||||
|
fn flakes(
|
||||||
|
self,
|
||||||
|
settings: &FlakeSettings,
|
||||||
|
) -> Result<nix_bindings_expr::eval_state::EvalStateBuilder>;
|
||||||
|
}
|
||||||
|
impl EvalStateBuilderExt for nix_bindings_expr::eval_state::EvalStateBuilder {
|
||||||
|
/// Configures the eval state to provide flakes features such as `builtins.getFlake`.
|
||||||
|
fn flakes(
|
||||||
|
mut self,
|
||||||
|
settings: &FlakeSettings,
|
||||||
|
) -> Result<nix_bindings_expr::eval_state::EvalStateBuilder> {
|
||||||
|
settings.add_to_eval_state_builder(&mut self)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
42
nixide/src/flake/fetchers_settings.rs
Normal file
42
nixide/src/flake/fetchers_settings.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use crate::sys;
|
||||||
|
use crate::{ErrorContext, NixErrorCode};
|
||||||
|
|
||||||
|
pub(super) struct FetchersSettings {
|
||||||
|
pub(super) ptr: NonNull<sys::nix_fetchers_settings>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetchersSettings {
|
||||||
|
pub fn new() -> Result<Self, NixErrorCode> {
|
||||||
|
let ctx = ErrorContext::new()?;
|
||||||
|
let ptr = unsafe { sys::nix_fetchers_settings_new(ctx.as_ptr()) };
|
||||||
|
Ok(FetchersSettings {
|
||||||
|
ptr: NonNull::new(ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
|
location: "fetchers_settings_new",
|
||||||
|
})?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_fetchers_settings {
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FetchersSettings {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
sys::nix_fetchers_settings_free(self.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fetchers_settings_new() {
|
||||||
|
let _ = FetchersSettings::new().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
61
nixide/src/flake/flake_lock_flags.rs
Normal file
61
nixide/src/flake/flake_lock_flags.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/// Parameters that affect the locking of a flake.
|
||||||
|
pub struct FlakeLockFlags {
|
||||||
|
pub(crate) ptr: *mut raw::flake_lock_flags,
|
||||||
|
}
|
||||||
|
impl Drop for FlakeLockFlags {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
raw::flake_lock_flags_free(self.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FlakeLockFlags {
|
||||||
|
pub fn new(settings: &FlakeSettings) -> Result<Self> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
let s = unsafe { context::check_call!(raw::flake_lock_flags_new(&mut ctx, settings.ptr)) }?;
|
||||||
|
Ok(FlakeLockFlags { ptr: s })
|
||||||
|
}
|
||||||
|
/// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file.
|
||||||
|
pub fn set_mode_write_as_needed(&mut self) -> Result<()> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
unsafe {
|
||||||
|
context::check_call!(raw::flake_lock_flags_set_mode_write_as_needed(
|
||||||
|
&mut ctx, self.ptr
|
||||||
|
))
|
||||||
|
}?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Make [LockedFlake::lock] check if the lock file is up to date. If not, an error is returned.
|
||||||
|
pub fn set_mode_check(&mut self) -> Result<()> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
unsafe { context::check_call!(raw::flake_lock_flags_set_mode_check(&mut ctx, self.ptr)) }?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Like `set_mode_write_as_needed`, but does not write to the lock file.
|
||||||
|
pub fn set_mode_virtual(&mut self) -> Result<()> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
unsafe {
|
||||||
|
context::check_call!(raw::flake_lock_flags_set_mode_virtual(&mut ctx, self.ptr))
|
||||||
|
}?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Adds an input override to the lock file that will be produced. The [LockedFlake::lock] operation will not write to the lock file.
|
||||||
|
pub fn add_input_override(
|
||||||
|
&mut self,
|
||||||
|
override_path: &str,
|
||||||
|
override_ref: &FlakeReference,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
unsafe {
|
||||||
|
context::check_call!(raw::flake_lock_flags_add_input_override(
|
||||||
|
&mut ctx,
|
||||||
|
self.ptr,
|
||||||
|
CString::new(override_path)
|
||||||
|
.context("Failed to create CString for override_path")?
|
||||||
|
.as_ptr(),
|
||||||
|
override_ref.ptr.as_ptr()
|
||||||
|
))
|
||||||
|
}?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
43
nixide/src/flake/flake_reference.rs
Normal file
43
nixide/src/flake/flake_reference.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
pub struct FlakeReference {
|
||||||
|
pub(crate) ptr: NonNull<raw::flake_reference>,
|
||||||
|
}
|
||||||
|
impl Drop for FlakeReference {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
raw::flake_reference_free(self.ptr.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FlakeReference {
|
||||||
|
/// Parse a flake reference from a string.
|
||||||
|
/// The string must be a valid flake reference, such as `github:owner/repo`.
|
||||||
|
/// It may also be suffixed with a `#` and a fragment, such as `github:owner/repo#something`,
|
||||||
|
/// in which case, the returned string will contain the fragment.
|
||||||
|
pub fn parse_with_fragment(
|
||||||
|
fetch_settings: &FetchersSettings,
|
||||||
|
flake_settings: &FlakeSettings,
|
||||||
|
flags: &FlakeReferenceParseFlags,
|
||||||
|
reference: &str,
|
||||||
|
) -> Result<(FlakeReference, String)> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
let mut r = result_string_init!();
|
||||||
|
let mut ptr: *mut raw::flake_reference = std::ptr::null_mut();
|
||||||
|
unsafe {
|
||||||
|
context::check_call!(raw::flake_reference_and_fragment_from_string(
|
||||||
|
&mut ctx,
|
||||||
|
fetch_settings.raw_ptr(),
|
||||||
|
flake_settings.ptr,
|
||||||
|
flags.ptr.as_ptr(),
|
||||||
|
reference.as_ptr() as *const c_char,
|
||||||
|
reference.len(),
|
||||||
|
// pointer to ptr
|
||||||
|
&mut ptr,
|
||||||
|
Some(callback_get_result_string),
|
||||||
|
callback_get_result_string_data(&mut r)
|
||||||
|
))
|
||||||
|
}?;
|
||||||
|
let ptr = NonNull::new(ptr)
|
||||||
|
.context("flake_reference_and_fragment_from_string unexpectedly returned null")?;
|
||||||
|
Ok((FlakeReference { ptr }, r?))
|
||||||
|
}
|
||||||
|
}
|
||||||
45
nixide/src/flake/flake_reference_parse_flags.rs
Normal file
45
nixide/src/flake/flake_reference_parse_flags.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use super::FlakeSettings;
|
||||||
|
use crate::sys;
|
||||||
|
use crate::{ErrorContext, NixErrorCode};
|
||||||
|
|
||||||
|
/// Parameters for parsing a flake reference.
|
||||||
|
pub struct FlakeReferenceParseFlags {
|
||||||
|
pub(crate) ptr: NonNull<sys::nix_flake_reference_parse_flags>,
|
||||||
|
}
|
||||||
|
impl Drop for FlakeReferenceParseFlags {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
sys::nix_flake_reference_parse_flags_free(self.ptr.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FlakeReferenceParseFlags {
|
||||||
|
pub fn new(settings: &FlakeSettings) -> Result<Self, NixErrorCode> {
|
||||||
|
let mut ctx = ErrorContext::new();
|
||||||
|
let ptr = unsafe {
|
||||||
|
context::check_call!(sys::nix_flake_reference_parse_flags_new(
|
||||||
|
&mut ctx,
|
||||||
|
settings.ptr
|
||||||
|
))
|
||||||
|
}?;
|
||||||
|
let ptr = NonNull::new(ptr)
|
||||||
|
.context("flake_reference_parse_flags_new unexpectedly returned null")?;
|
||||||
|
Ok(FlakeReferenceParseFlags { ptr })
|
||||||
|
}
|
||||||
|
/// Sets the [base directory](https://nix.dev/manual/nix/latest/glossary#gloss-base-directory)
|
||||||
|
/// for resolving local flake references.
|
||||||
|
pub fn set_base_directory(&mut self, base_directory: &str) -> Result<(), NixErrorCode> {
|
||||||
|
let mut ctx = ErrorContext::new();
|
||||||
|
unsafe {
|
||||||
|
sys::context::check_call!(sys::nix_flake_reference_parse_flags_set_base_directory(
|
||||||
|
&mut ctx,
|
||||||
|
self.ptr.as_ptr(),
|
||||||
|
base_directory.as_ptr() as *const c_char,
|
||||||
|
base_directory.len()
|
||||||
|
))
|
||||||
|
}?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
52
nixide/src/flake/flake_settings.rs
Normal file
52
nixide/src/flake/flake_settings.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use crate::sys;
|
||||||
|
use crate::{ErrorContext, EvalStateBuilder, NixErrorCode};
|
||||||
|
|
||||||
|
/// Store settings for the flakes feature.
|
||||||
|
pub struct FlakeSettings {
|
||||||
|
pub(crate) inner: NonNull<sys::nix_flake_settings>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlakeSettings {
|
||||||
|
pub fn new() -> Result<Self, NixErrorCode> {
|
||||||
|
let ctx = ErrorContext::new()?;
|
||||||
|
let inner = NonNull::new(unsafe { sys::nix_flake_settings_new(ctx.as_ptr()) }).ok_or(
|
||||||
|
NixErrorCode::NullPtr {
|
||||||
|
location: "nix_flake_settings_new",
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(FlakeSettings { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_to_eval_state_builder(
|
||||||
|
&self,
|
||||||
|
builder: &mut EvalStateBuilder,
|
||||||
|
) -> Result<(), NixErrorCode> {
|
||||||
|
let ctx = ErrorContext::new()?;
|
||||||
|
NixErrorCode::from(
|
||||||
|
unsafe {
|
||||||
|
sys::nix_flake_settings_add_to_eval_state_builder(
|
||||||
|
ctx.as_ptr(),
|
||||||
|
self.as_ptr(),
|
||||||
|
builder.as_ptr(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"nix_flake_settings_add_to_eval_state_builder",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_flake_settings {
|
||||||
|
self.inner.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FlakeSettings {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
sys::nix_flake_settings_free(self.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
nixide/src/flake/flakeref.rs
Normal file
3
nixide/src/flake/flakeref.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub struct FlakeRef {}
|
||||||
|
|
||||||
|
impl FlakeRef {}
|
||||||
409
nixide/src/flake/locked_flake.rs
Normal file
409
nixide/src/flake/locked_flake.rs
Normal file
|
|
@ -0,0 +1,409 @@
|
||||||
|
pub struct LockedFlake {
|
||||||
|
pub(crate) ptr: NonNull<raw::locked_flake>,
|
||||||
|
}
|
||||||
|
impl Drop for LockedFlake {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
raw::locked_flake_free(self.ptr.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl LockedFlake {
|
||||||
|
pub fn lock(
|
||||||
|
fetch_settings: &FetchersSettings,
|
||||||
|
flake_settings: &FlakeSettings,
|
||||||
|
eval_state: &EvalState,
|
||||||
|
flags: &FlakeLockFlags,
|
||||||
|
flake_ref: &FlakeReference,
|
||||||
|
) -> Result<LockedFlake> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
let ptr = unsafe {
|
||||||
|
context::check_call!(raw::flake_lock(
|
||||||
|
&mut ctx,
|
||||||
|
fetch_settings.raw_ptr(),
|
||||||
|
flake_settings.ptr,
|
||||||
|
eval_state.raw_ptr(),
|
||||||
|
flags.ptr,
|
||||||
|
flake_ref.ptr.as_ptr()
|
||||||
|
))
|
||||||
|
}?;
|
||||||
|
let ptr = NonNull::new(ptr).context("flake_lock unexpectedly returned null")?;
|
||||||
|
Ok(LockedFlake { ptr })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the outputs of the flake - the result of calling the `outputs` attribute.
|
||||||
|
pub fn outputs(
|
||||||
|
&self,
|
||||||
|
flake_settings: &FlakeSettings,
|
||||||
|
eval_state: &mut EvalState,
|
||||||
|
) -> Result<nix_bindings_expr::value::Value> {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
unsafe {
|
||||||
|
let r = context::check_call!(raw::locked_flake_get_output_attrs(
|
||||||
|
&mut ctx,
|
||||||
|
flake_settings.ptr,
|
||||||
|
eval_state.raw_ptr(),
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
))?;
|
||||||
|
Ok(nix_bindings_expr::value::__private::raw_value_new(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use nix_bindings_expr::eval_state::{gc_register_my_thread, EvalStateBuilder};
|
||||||
|
use nix_bindings_store::store::Store;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
// Only set experimental-features once to minimize the window where
|
||||||
|
// concurrent Nix operations might read the setting while it's being modified
|
||||||
|
INIT.call_once(|| {
|
||||||
|
nix_bindings_expr::eval_state::init().unwrap();
|
||||||
|
nix_bindings_util::settings::set("experimental-features", "flakes").unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flake_settings_getflake_exists() {
|
||||||
|
init();
|
||||||
|
let gc_registration = gc_register_my_thread();
|
||||||
|
let store = Store::open(None, []).unwrap();
|
||||||
|
let mut eval_state = EvalStateBuilder::new(store)
|
||||||
|
.unwrap()
|
||||||
|
.flakes(&FlakeSettings::new().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v = eval_state
|
||||||
|
.eval_from_string("builtins?getFlake", "<test>")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let b = eval_state.require_bool(&v).unwrap();
|
||||||
|
|
||||||
|
assert!(b);
|
||||||
|
|
||||||
|
drop(gc_registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flake_lock_load_flake() {
|
||||||
|
init();
|
||||||
|
let gc_registration = gc_register_my_thread();
|
||||||
|
let store = Store::open(None, []).unwrap();
|
||||||
|
let fetchers_settings = FetchersSettings::new().unwrap();
|
||||||
|
let flake_settings = FlakeSettings::new().unwrap();
|
||||||
|
let mut eval_state = EvalStateBuilder::new(store)
|
||||||
|
.unwrap()
|
||||||
|
.flakes(&flake_settings)
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tmp_dir = tempfile::tempdir().unwrap();
|
||||||
|
|
||||||
|
// Create flake.nix
|
||||||
|
let flake_nix = tmp_dir.path().join("flake.nix");
|
||||||
|
std::fs::write(
|
||||||
|
&flake_nix,
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
outputs = { ... }: {
|
||||||
|
hello = "potato";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap();
|
||||||
|
|
||||||
|
let (flake_ref, fragment) = FlakeReference::parse_with_fragment(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&FlakeReferenceParseFlags::new(&flake_settings).unwrap(),
|
||||||
|
&format!("path:{}#subthing", tmp_dir.path().display()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(fragment, "subthing");
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let outputs = locked_flake
|
||||||
|
.outputs(&flake_settings, &mut eval_state)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
|
||||||
|
let hello = eval_state.require_string(&hello).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(hello, "potato");
|
||||||
|
|
||||||
|
drop(fetchers_settings);
|
||||||
|
drop(tmp_dir);
|
||||||
|
drop(gc_registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flake_lock_load_flake_with_flags() {
|
||||||
|
init();
|
||||||
|
let gc_registration = gc_register_my_thread();
|
||||||
|
let store = Store::open(None, []).unwrap();
|
||||||
|
let fetchers_settings = FetchersSettings::new().unwrap();
|
||||||
|
let flake_settings = FlakeSettings::new().unwrap();
|
||||||
|
let mut eval_state = EvalStateBuilder::new(store)
|
||||||
|
.unwrap()
|
||||||
|
.flakes(&flake_settings)
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tmp_dir = tempfile::tempdir().unwrap();
|
||||||
|
|
||||||
|
let flake_dir_a = tmp_dir.path().join("a");
|
||||||
|
let flake_dir_b = tmp_dir.path().join("b");
|
||||||
|
let flake_dir_c = tmp_dir.path().join("c");
|
||||||
|
|
||||||
|
std::fs::create_dir_all(&flake_dir_a).unwrap();
|
||||||
|
std::fs::create_dir_all(&flake_dir_b).unwrap();
|
||||||
|
std::fs::create_dir_all(&flake_dir_c).unwrap();
|
||||||
|
|
||||||
|
let flake_dir_a_str = flake_dir_a.to_str().unwrap();
|
||||||
|
let flake_dir_c_str = flake_dir_c.to_str().unwrap();
|
||||||
|
assert!(!flake_dir_a_str.is_empty());
|
||||||
|
assert!(!flake_dir_c_str.is_empty());
|
||||||
|
|
||||||
|
// a
|
||||||
|
std::fs::write(
|
||||||
|
tmp_dir.path().join("a/flake.nix"),
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
inputs.b.url = "@flake_dir_b@";
|
||||||
|
outputs = { b, ... }: {
|
||||||
|
hello = b.hello;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.replace("@flake_dir_b@", flake_dir_b.to_str().unwrap()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// b
|
||||||
|
std::fs::write(
|
||||||
|
tmp_dir.path().join("b/flake.nix"),
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
outputs = { ... }: {
|
||||||
|
hello = "BOB";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// c
|
||||||
|
std::fs::write(
|
||||||
|
tmp_dir.path().join("c/flake.nix"),
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
outputs = { ... }: {
|
||||||
|
hello = "Claire";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap();
|
||||||
|
|
||||||
|
let mut flake_reference_parse_flags =
|
||||||
|
FlakeReferenceParseFlags::new(&flake_settings).unwrap();
|
||||||
|
|
||||||
|
flake_reference_parse_flags
|
||||||
|
.set_base_directory(tmp_dir.path().to_str().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (flake_ref_a, fragment) = FlakeReference::parse_with_fragment(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&flake_reference_parse_flags,
|
||||||
|
&format!("path:{}", &flake_dir_a_str),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(fragment, "");
|
||||||
|
|
||||||
|
// Step 1: Do not update (check), fails
|
||||||
|
|
||||||
|
flake_lock_flags.set_mode_check().unwrap();
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref_a,
|
||||||
|
);
|
||||||
|
// Has not been locked and would need to write a lock file.
|
||||||
|
assert!(locked_flake.is_err());
|
||||||
|
let saved_err = match locked_flake {
|
||||||
|
Ok(_) => panic!("Expected error, but got Ok"),
|
||||||
|
Err(e) => e,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 2: Update but do not write, succeeds
|
||||||
|
flake_lock_flags.set_mode_virtual().unwrap();
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref_a,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let outputs = locked_flake
|
||||||
|
.outputs(&flake_settings, &mut eval_state)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
|
||||||
|
let hello = eval_state.require_string(&hello).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(hello, "BOB");
|
||||||
|
|
||||||
|
// Step 3: The lock was not written, so Step 1 would fail again
|
||||||
|
|
||||||
|
flake_lock_flags.set_mode_check().unwrap();
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref_a,
|
||||||
|
);
|
||||||
|
// Has not been locked and would need to write a lock file.
|
||||||
|
assert!(locked_flake.is_err());
|
||||||
|
match locked_flake {
|
||||||
|
Ok(_) => panic!("Expected error, but got Ok"),
|
||||||
|
Err(e) => {
|
||||||
|
assert_eq!(e.to_string(), saved_err.to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 4: Update and write, succeeds
|
||||||
|
|
||||||
|
flake_lock_flags.set_mode_write_as_needed().unwrap();
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref_a,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let outputs = locked_flake
|
||||||
|
.outputs(&flake_settings, &mut eval_state)
|
||||||
|
.unwrap();
|
||||||
|
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
|
||||||
|
let hello = eval_state.require_string(&hello).unwrap();
|
||||||
|
assert_eq!(hello, "BOB");
|
||||||
|
|
||||||
|
// Step 5: Lock was written, so Step 1 succeeds
|
||||||
|
|
||||||
|
flake_lock_flags.set_mode_check().unwrap();
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref_a,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let outputs = locked_flake
|
||||||
|
.outputs(&flake_settings, &mut eval_state)
|
||||||
|
.unwrap();
|
||||||
|
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
|
||||||
|
let hello = eval_state.require_string(&hello).unwrap();
|
||||||
|
assert_eq!(hello, "BOB");
|
||||||
|
|
||||||
|
// Step 6: Lock with override, do not write
|
||||||
|
|
||||||
|
// This shouldn't matter; write_as_needed will be overridden
|
||||||
|
flake_lock_flags.set_mode_write_as_needed().unwrap();
|
||||||
|
|
||||||
|
let (flake_ref_c, fragment) = FlakeReference::parse_with_fragment(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&flake_reference_parse_flags,
|
||||||
|
&format!("path:{}", &flake_dir_c_str),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(fragment, "");
|
||||||
|
|
||||||
|
flake_lock_flags
|
||||||
|
.add_input_override("b", &flake_ref_c)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref_a,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let outputs = locked_flake
|
||||||
|
.outputs(&flake_settings, &mut eval_state)
|
||||||
|
.unwrap();
|
||||||
|
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
|
||||||
|
let hello = eval_state.require_string(&hello).unwrap();
|
||||||
|
assert_eq!(hello, "Claire");
|
||||||
|
|
||||||
|
// Can't delete overrides, so trash it
|
||||||
|
let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap();
|
||||||
|
|
||||||
|
// Step 7: Override was not written; lock still points to b
|
||||||
|
|
||||||
|
flake_lock_flags.set_mode_check().unwrap();
|
||||||
|
|
||||||
|
let locked_flake = LockedFlake::lock(
|
||||||
|
&fetchers_settings,
|
||||||
|
&flake_settings,
|
||||||
|
&eval_state,
|
||||||
|
&flake_lock_flags,
|
||||||
|
&flake_ref_a,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let outputs = locked_flake
|
||||||
|
.outputs(&flake_settings, &mut eval_state)
|
||||||
|
.unwrap();
|
||||||
|
let hello = eval_state.require_attrs_select(&outputs, "hello").unwrap();
|
||||||
|
let hello = eval_state.require_string(&hello).unwrap();
|
||||||
|
assert_eq!(hello, "BOB");
|
||||||
|
|
||||||
|
drop(fetchers_settings);
|
||||||
|
drop(tmp_dir);
|
||||||
|
drop(gc_registration);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
nixide/src/flake/mod.rs
Normal file
15
nixide/src/flake/mod.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
mod eval_state_builder_ext;
|
||||||
|
mod fetchers_settings;
|
||||||
|
mod flake_lock_flags;
|
||||||
|
mod flake_reference;
|
||||||
|
mod flake_reference_parse_flags;
|
||||||
|
mod flake_settings;
|
||||||
|
mod locked_flake;
|
||||||
|
|
||||||
|
pub(self) use eval_state_builder_ext::EvalStateBuilderExt;
|
||||||
|
pub(self) use fetchers_settings::FetchersSettings;
|
||||||
|
pub(self) use flake_lock_flags::FlakeLockFlags;
|
||||||
|
pub(self) use flake_reference::FlakeReference;
|
||||||
|
pub(self) use flake_reference_parse_flags::FlakeReferenceParseFlags;
|
||||||
|
pub(self) use flake_settings::FlakeSettings;
|
||||||
|
pub(self) use locked_flake::LockedFlake;
|
||||||
|
|
@ -3,14 +3,28 @@
|
||||||
mod context;
|
mod context;
|
||||||
mod error;
|
mod error;
|
||||||
mod expr;
|
mod expr;
|
||||||
|
mod flake;
|
||||||
mod store;
|
mod store;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
mod verbosity;
|
||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
pub use context::Context;
|
pub use context::ErrorContext;
|
||||||
pub use error::NixError;
|
pub use error::NixErrorCode;
|
||||||
pub use expr::{EvalState, EvalStateBuilder, Value, ValueType};
|
pub use expr::{EvalState, EvalStateBuilder, Value, ValueType};
|
||||||
pub use store::{Store, StorePath};
|
pub use store::{Store, StorePath};
|
||||||
pub use version::*;
|
pub use verbosity::NixVerbosity;
|
||||||
|
pub use version::NixVersion;
|
||||||
|
|
||||||
pub use nixide_sys as sys;
|
pub use nixide_sys as sys;
|
||||||
|
|
||||||
|
/// Sets the verbosity level
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `context` - additional error context, used as an output
|
||||||
|
/// * `level` - verbosity level
|
||||||
|
pub fn set_verbosity() {
|
||||||
|
// nix_err nix_set_verbosity(nix_c_context * context, nix_verbosity level);
|
||||||
|
// XXX: TODO: (implement Context first)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use std::ptr::NonNull;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Context, NixError};
|
use super::{ErrorContext, NixErrorCode};
|
||||||
use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback};
|
use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback};
|
||||||
use nixide_sys as sys;
|
use nixide_sys as sys;
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ use nixide_sys as sys;
|
||||||
/// The store provides access to Nix packages, derivations, and store paths.
|
/// The store provides access to Nix packages, derivations, and store paths.
|
||||||
pub struct Store {
|
pub struct Store {
|
||||||
pub(crate) inner: NonNull<sys::Store>,
|
pub(crate) inner: NonNull<sys::Store>,
|
||||||
pub(crate) _context: Arc<Context>,
|
pub(crate) _context: Arc<ErrorContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
|
|
@ -46,10 +46,10 @@ impl Store {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the store cannot be opened.
|
/// Returns an error if the store cannot be opened.
|
||||||
pub fn open(context: &Arc<Context>, uri: Option<&str>) -> Result<Self, NixError> {
|
pub fn open(context: &Arc<ErrorContext>, uri: Option<&str>) -> Result<Self, NixErrorCode> {
|
||||||
let uri_cstring: CString;
|
let uri_cstring: CString;
|
||||||
let uri_ptr = if let Some(uri) = uri {
|
let uri_ptr = if let Some(uri) = uri {
|
||||||
uri_cstring = NixError::from_nulerror(CString::new(uri), "nixide::Store::open")?;
|
uri_cstring = NixErrorCode::from_nulerror(CString::new(uri), "nixide::Store::open")?;
|
||||||
uri_cstring.as_ptr()
|
uri_cstring.as_ptr()
|
||||||
} else {
|
} else {
|
||||||
std::ptr::null()
|
std::ptr::null()
|
||||||
|
|
@ -59,7 +59,7 @@ impl Store {
|
||||||
let store_ptr =
|
let store_ptr =
|
||||||
unsafe { sys::nix_store_open(context.as_ptr(), uri_ptr, std::ptr::null_mut()) };
|
unsafe { sys::nix_store_open(context.as_ptr(), uri_ptr, std::ptr::null_mut()) };
|
||||||
|
|
||||||
let inner = NonNull::new(store_ptr).ok_or(NixError::NullPtr {
|
let inner = NonNull::new(store_ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
location: "nix_store_open",
|
location: "nix_store_open",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -99,9 +99,13 @@ impl Store {
|
||||||
&self,
|
&self,
|
||||||
path: &StorePath,
|
path: &StorePath,
|
||||||
callback: fn(&str, &StorePath),
|
callback: fn(&str, &StorePath),
|
||||||
) -> Result<Vec<(String, StorePath)>, NixError> {
|
) -> Result<Vec<(String, StorePath)>, NixErrorCode> {
|
||||||
// Type alias for our userdata: (outputs vector, context)
|
// Type alias for our userdata: (outputs vector, context)
|
||||||
type Userdata = (Vec<(String, StorePath)>, Arc<Context>, fn(&str, &StorePath));
|
type Userdata = (
|
||||||
|
Vec<(String, StorePath)>,
|
||||||
|
Arc<ErrorContext>,
|
||||||
|
fn(&str, &StorePath),
|
||||||
|
);
|
||||||
|
|
||||||
// Callback function that will be called for each realized output
|
// Callback function that will be called for each realized output
|
||||||
unsafe extern "C" fn realise_callback(
|
unsafe extern "C" fn realise_callback(
|
||||||
|
|
@ -157,7 +161,7 @@ impl Store {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
NixError::from(err, "nix_store_realise")?;
|
NixErrorCode::from(err, "nix_store_realise")?;
|
||||||
|
|
||||||
// Return the collected outputs
|
// Return the collected outputs
|
||||||
Ok(userdata.0)
|
Ok(userdata.0)
|
||||||
|
|
@ -187,14 +191,14 @@ impl Store {
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn store_path(&self, path: &str) -> Result<StorePath, NixError> {
|
pub fn store_path(&self, path: &str) -> Result<StorePath, NixErrorCode> {
|
||||||
StorePath::parse(&self._context, self, path)
|
StorePath::parse(&self._context, self, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the version of a Nix store
|
/// Get the version of a Nix store
|
||||||
///
|
///
|
||||||
/// If the store doesn't have a version (like the dummy store), returns None
|
/// If the store doesn't have a version (like the dummy store), returns None
|
||||||
pub fn version(&self) -> Result<String, NixError> {
|
pub fn version(&self) -> Result<String, NixErrorCode> {
|
||||||
wrap_libnix_string_callback("nix_store_get_version", |callback, user_data| unsafe {
|
wrap_libnix_string_callback("nix_store_get_version", |callback, user_data| unsafe {
|
||||||
sys::nix_store_get_version(
|
sys::nix_store_get_version(
|
||||||
self._context.as_ptr(),
|
self._context.as_ptr(),
|
||||||
|
|
@ -206,7 +210,7 @@ impl Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the URI of a Nix store
|
/// Get the URI of a Nix store
|
||||||
pub fn uri(&self) -> Result<String, NixError> {
|
pub fn uri(&self) -> Result<String, NixErrorCode> {
|
||||||
wrap_libnix_string_callback("nix_store_get_uri", |callback, user_data| unsafe {
|
wrap_libnix_string_callback("nix_store_get_uri", |callback, user_data| unsafe {
|
||||||
sys::nix_store_get_uri(
|
sys::nix_store_get_uri(
|
||||||
self._context.as_ptr(),
|
self._context.as_ptr(),
|
||||||
|
|
@ -217,7 +221,7 @@ impl Store {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store_dir(&self) -> Result<PathBuf, NixError> {
|
pub fn store_dir(&self) -> Result<PathBuf, NixErrorCode> {
|
||||||
wrap_libnix_pathbuf_callback("nix_store_get_storedir", |callback, user_data| unsafe {
|
wrap_libnix_pathbuf_callback("nix_store_get_storedir", |callback, user_data| unsafe {
|
||||||
sys::nix_store_get_storedir(
|
sys::nix_store_get_storedir(
|
||||||
self._context.as_ptr(),
|
self._context.as_ptr(),
|
||||||
|
|
@ -232,7 +236,7 @@ impl Store {
|
||||||
&self,
|
&self,
|
||||||
dst_store: &Store,
|
dst_store: &Store,
|
||||||
store_path: &StorePath,
|
store_path: &StorePath,
|
||||||
) -> Result<(), NixError> {
|
) -> Result<(), NixErrorCode> {
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
sys::nix_store_copy_closure(
|
sys::nix_store_copy_closure(
|
||||||
self._context.as_ptr(),
|
self._context.as_ptr(),
|
||||||
|
|
@ -241,14 +245,14 @@ impl Store {
|
||||||
store_path.inner.as_ptr(),
|
store_path.inner.as_ptr(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
NixError::from(err, "nix_store_copy_closure")
|
NixErrorCode::from(err, "nix_store_copy_closure")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_closure_from(
|
pub fn copy_closure_from(
|
||||||
&self,
|
&self,
|
||||||
src_store: &Store,
|
src_store: &Store,
|
||||||
store_path: &StorePath,
|
store_path: &StorePath,
|
||||||
) -> Result<(), NixError> {
|
) -> Result<(), NixErrorCode> {
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
sys::nix_store_copy_closure(
|
sys::nix_store_copy_closure(
|
||||||
self._context.as_ptr(),
|
self._context.as_ptr(),
|
||||||
|
|
@ -257,7 +261,7 @@ impl Store {
|
||||||
store_path.inner.as_ptr(),
|
store_path.inner.as_ptr(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
NixError::from(err, "nix_store_copy_closure")
|
NixErrorCode::from(err, "nix_store_copy_closure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use super::Store;
|
use super::Store;
|
||||||
use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback};
|
use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback};
|
||||||
use crate::{Context, NixError};
|
use crate::{ErrorContext, NixErrorCode};
|
||||||
use nixide_sys::{self as sys, nix_err_NIX_OK};
|
use nixide_sys::{self as sys, nix_err_NIX_OK};
|
||||||
|
|
||||||
/// A path in the Nix store.
|
/// A path in the Nix store.
|
||||||
|
|
@ -13,7 +13,7 @@ use nixide_sys::{self as sys, nix_err_NIX_OK};
|
||||||
/// Represents a store path that can be realized, queried, or manipulated.
|
/// Represents a store path that can be realized, queried, or manipulated.
|
||||||
pub struct StorePath {
|
pub struct StorePath {
|
||||||
pub(crate) inner: NonNull<sys::StorePath>,
|
pub(crate) inner: NonNull<sys::StorePath>,
|
||||||
pub(crate) _context: Arc<Context>,
|
pub(crate) _context: Arc<ErrorContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorePath {
|
impl StorePath {
|
||||||
|
|
@ -28,8 +28,12 @@ impl StorePath {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the path cannot be parsed.
|
/// Returns an error if the path cannot be parsed.
|
||||||
pub fn parse(context: &Arc<Context>, store: &Store, path: &str) -> Result<Self, NixError> {
|
pub fn parse(
|
||||||
let path_cstring = CString::new(path).or(Err(NixError::InvalidArg {
|
context: &Arc<ErrorContext>,
|
||||||
|
store: &Store,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<Self, NixErrorCode> {
|
||||||
|
let path_cstring = CString::new(path).or(Err(NixErrorCode::InvalidArg {
|
||||||
location: "nixide::StorePath::parse",
|
location: "nixide::StorePath::parse",
|
||||||
reason: "`path` contains NUL char",
|
reason: "`path` contains NUL char",
|
||||||
}))?;
|
}))?;
|
||||||
|
|
@ -39,7 +43,7 @@ impl StorePath {
|
||||||
sys::nix_store_parse_path(context.as_ptr(), store.as_ptr(), path_cstring.as_ptr())
|
sys::nix_store_parse_path(context.as_ptr(), store.as_ptr(), path_cstring.as_ptr())
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = NonNull::new(path_ptr).ok_or(NixError::NullPtr {
|
let inner = NonNull::new(path_ptr).ok_or(NixErrorCode::NullPtr {
|
||||||
location: "nix_store_parse_path",
|
location: "nix_store_parse_path",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -57,7 +61,7 @@ impl StorePath {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the name cannot be retrieved.
|
/// Returns an error if the name cannot be retrieved.
|
||||||
pub fn name(&self) -> Result<String, NixError> {
|
pub fn name(&self) -> Result<String, NixErrorCode> {
|
||||||
wrap_libnix_string_callback("nix_store_path_name", |callback, user_data| unsafe {
|
wrap_libnix_string_callback("nix_store_path_name", |callback, user_data| unsafe {
|
||||||
sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data);
|
sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data);
|
||||||
|
|
||||||
|
|
@ -87,7 +91,7 @@ impl StorePath {
|
||||||
///
|
///
|
||||||
/// * `store` - The store containing the path
|
/// * `store` - The store containing the path
|
||||||
///
|
///
|
||||||
pub fn real_path(&self, store: &Store) -> Result<PathBuf, NixError> {
|
pub fn real_path(&self, store: &Store) -> Result<PathBuf, NixErrorCode> {
|
||||||
wrap_libnix_pathbuf_callback("nix_store_real_path", |callback, user_data| unsafe {
|
wrap_libnix_pathbuf_callback("nix_store_real_path", |callback, user_data| unsafe {
|
||||||
sys::nix_store_real_path(
|
sys::nix_store_real_path(
|
||||||
self._context.as_ptr(),
|
self._context.as_ptr(),
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_store_opening() {
|
fn test_store_opening() {
|
||||||
let ctx = Arc::new(Context::new().expect("Failed to create context"));
|
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
||||||
let _store = Store::open(&ctx, None).expect("Failed to open store");
|
let _store = Store::open(&ctx, None).expect("Failed to open store");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_store_path_parse() {
|
fn test_store_path_parse() {
|
||||||
let ctx = Arc::new(Context::new().expect("Failed to create context"));
|
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
||||||
let store = Store::open(&ctx, None).expect("Failed to open store");
|
let store = Store::open(&ctx, None).expect("Failed to open store");
|
||||||
|
|
||||||
// Try parsing a well-formed store path
|
// Try parsing a well-formed store path
|
||||||
|
|
@ -38,7 +38,7 @@ fn test_store_path_parse() {
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_store_path_clone() {
|
fn test_store_path_clone() {
|
||||||
let ctx = Arc::new(Context::new().expect("Failed to create context"));
|
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
||||||
let store = Store::open(&ctx, None).expect("Failed to open store");
|
let store = Store::open(&ctx, None).expect("Failed to open store");
|
||||||
|
|
||||||
// Try to get a valid store path by parsing
|
// Try to get a valid store path by parsing
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
use std::os::raw::{c_char, c_uint, c_void};
|
use std::os::raw::{c_char, c_uint, c_void};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::NixError;
|
use crate::NixErrorCode;
|
||||||
|
|
||||||
pub fn wrap_libnix_string_callback<F>(name: &'static str, callback: F) -> Result<String, NixError>
|
pub fn wrap_libnix_string_callback<F>(
|
||||||
|
name: &'static str,
|
||||||
|
callback: F,
|
||||||
|
) -> Result<String, NixErrorCode>
|
||||||
where
|
where
|
||||||
F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32,
|
F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32,
|
||||||
{
|
{
|
||||||
|
|
@ -22,11 +25,14 @@ where
|
||||||
let mut result: Option<String> = None;
|
let mut result: Option<String> = None;
|
||||||
let user_data = &mut result as *mut _ as *mut c_void;
|
let user_data = &mut result as *mut _ as *mut c_void;
|
||||||
|
|
||||||
NixError::from(callback(wrapper_callback, user_data), name)?;
|
NixErrorCode::from(callback(wrapper_callback, user_data), name)?;
|
||||||
result.ok_or(NixError::NullPtr { location: name })
|
result.ok_or(NixErrorCode::NullPtr { location: name })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap_libnix_pathbuf_callback<F>(name: &'static str, callback: F) -> Result<PathBuf, NixError>
|
pub fn wrap_libnix_pathbuf_callback<F>(
|
||||||
|
name: &'static str,
|
||||||
|
callback: F,
|
||||||
|
) -> Result<PathBuf, NixErrorCode>
|
||||||
where
|
where
|
||||||
F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32,
|
F: FnOnce(unsafe extern "C" fn(*const c_char, c_uint, *mut c_void), *mut c_void) -> i32,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
34
nixide/src/verbosity.rs
Normal file
34
nixide/src/verbosity.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::sys;
|
||||||
|
|
||||||
|
/// Verbosity level
|
||||||
|
///
|
||||||
|
/// # NOTE
|
||||||
|
///
|
||||||
|
/// This should be kept in sync with the C++ implementation (nix::Verbosity)
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum NixVerbosity {
|
||||||
|
Error,
|
||||||
|
Warn,
|
||||||
|
Notice,
|
||||||
|
Info,
|
||||||
|
Talkative,
|
||||||
|
Chatty,
|
||||||
|
Debug,
|
||||||
|
Vomit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sys::nix_verbosity> for NixVerbosity {
|
||||||
|
fn from(level: sys::nix_verbosity) -> NixVerbosity {
|
||||||
|
match level {
|
||||||
|
sys::nix_verbosity_NIX_LVL_ERROR => NixVerbosity::Error,
|
||||||
|
sys::nix_verbosity_NIX_LVL_WARN => NixVerbosity::Warn,
|
||||||
|
sys::nix_verbosity_NIX_LVL_NOTICE => NixVerbosity::Notice,
|
||||||
|
sys::nix_verbosity_NIX_LVL_INFO => NixVerbosity::Info,
|
||||||
|
sys::nix_verbosity_NIX_LVL_TALKATIVE => NixVerbosity::Talkative,
|
||||||
|
sys::nix_verbosity_NIX_LVL_CHATTY => NixVerbosity::Chatty,
|
||||||
|
sys::nix_verbosity_NIX_LVL_DEBUG => NixVerbosity::Debug,
|
||||||
|
sys::nix_verbosity_NIX_LVL_VOMIT => NixVerbosity::Vomit,
|
||||||
|
_ => panic!("nixide encountered unknown `nix_verbosity` value, please submit this as an issue at https://github.com/cry128/nixide"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue