nixide/nixide/src/expr/realised_string.rs

104 lines
3.5 KiB
Rust

use std::cell::RefCell;
use std::ffi::c_char;
use std::ptr::NonNull;
use crate::errors::ErrorContext;
use crate::expr::values::NixString;
use crate::stdext::CCharPtrExt;
use crate::util::LazyArray;
use crate::util::wrappers::AsInnerPtr;
use crate::util::{panic_issue_call_failed, wrap};
use crate::{EvalState, NixideResult, StorePath};
use crate::{Store, sys};
pub struct RealisedString<'a> {
inner: RefCell<NonNull<sys::nix_realised_string>>,
pub path: StorePath,
pub children: LazyArray<StorePath, Box<dyn Fn(usize) -> StorePath + 'a>>,
}
impl<'a> AsInnerPtr<sys::nix_realised_string> for RealisedString<'a> {
#[inline]
unsafe fn as_ptr(&self) -> *mut sys::nix_realised_string {
self.inner.borrow().as_ptr()
}
#[inline]
unsafe fn as_ref(&self) -> &sys::nix_realised_string {
unsafe { self.inner.borrow().as_ref() }
}
#[inline]
unsafe fn as_mut(&mut self) -> &mut sys::nix_realised_string {
unsafe { self.inner.borrow_mut().as_mut() }
}
}
impl<'a> Drop for RealisedString<'a> {
fn drop(&mut self) {
unsafe {
sys::nix_realised_string_free(self.as_ptr());
}
}
}
impl<'a> RealisedString<'a> {
/// Realise a string context.
///
/// This will
/// - realise the store paths referenced by the string's content, and
/// - perform the replacement of placeholders.
/// - create temporary garbage collection roots for the store paths, for
/// the lifetime of the current process.
/// - log to stderr
///
/// # Arguments
///
/// * value - Nix value, which must be a string
/// * state - Nix evaluator state
/// * isIFD - If true, disallow derivation outputs if setting `allow-import-from-derivation` is false.
/// You should set this to true when this call is part of a primop.
/// You should set this to false when building for your application's purpose.
///
pub fn new(value: &NixString, state: &'a EvalState) -> NixideResult<RealisedString<'a>> {
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_string_realise(
ctx.as_ptr(),
state.as_ptr(),
value.as_ptr(),
false, // don't copy more
)
})?;
let size = unsafe { sys::nix_realised_string_get_store_path_count(inner.as_ptr()) };
Ok(Self {
inner: RefCell::new(inner),
path: Self::parse_path(inner.as_ptr(), &state.store_ref().borrow()),
children: LazyArray::new(
size,
Box::new(|_| StorePath::fake_path(&state.store_ref().borrow()).unwrap()),
),
})
}
fn parse_path(realised_string: *mut sys::nix_realised_string, store: &Store) -> StorePath {
let buffer_ptr = unsafe { sys::nix_realised_string_get_buffer_start(realised_string) };
let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_string) };
let path_str = (buffer_ptr as *const c_char)
.to_utf8_string_n(buffer_size)
.unwrap_or_else(|err| {
panic_issue_call_failed!(
"`sys::nix_realised_string_get_buffer_(start|size)` invalid UTF-8 ({})",
err
)
});
StorePath::parse(store, &path_str).unwrap_or_else(|err| {
panic_issue_call_failed!(
"`sys::nix_realised_string_get_buffer_(start|size)` invalid store path ({})",
err
)
})
}
}