use std::{ffi::CString, ptr::NonNull, sync::Arc};
use crate::{Context, Error, EvalState, Result, Value, check_err, sys};
pub struct FlakeSettings {
pub(crate) inner: NonNull<sys::nix_flake_settings>,
_context: Arc<Context>,
}
impl FlakeSettings {
pub fn new(context: &Arc<Context>) -> Result<Self> {
let ptr = unsafe { sys::nix_flake_settings_new(context.as_ptr()) };
let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
Ok(FlakeSettings {
inner,
_context: Arc::clone(context),
})
}
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.inner.as_ptr());
}
}
}
unsafe impl Send for FlakeSettings {}
unsafe impl Sync for FlakeSettings {}
pub struct FetchersSettings {
inner: NonNull<sys::nix_fetchers_settings>,
_context: Arc<Context>,
}
impl FetchersSettings {
pub fn new(context: &Arc<Context>) -> Result<Self> {
let ptr = unsafe { sys::nix_fetchers_settings_new(context.as_ptr()) };
let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
Ok(FetchersSettings {
inner,
_context: Arc::clone(context),
})
}
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_fetchers_settings {
self.inner.as_ptr()
}
}
impl Drop for FetchersSettings {
fn drop(&mut self) {
unsafe {
sys::nix_fetchers_settings_free(self.inner.as_ptr());
}
}
}
unsafe impl Send for FetchersSettings {}
unsafe impl Sync for FetchersSettings {}
pub struct FlakeReferenceParseFlags {
inner: NonNull<sys::nix_flake_reference_parse_flags>,
_context: Arc<Context>,
}
impl FlakeReferenceParseFlags {
pub fn new(
context: &Arc<Context>,
flake_settings: &FlakeSettings,
) -> Result<Self> {
let ptr = unsafe {
sys::nix_flake_reference_parse_flags_new(
context.as_ptr(),
flake_settings.as_ptr(),
)
};
let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
Ok(FlakeReferenceParseFlags {
inner,
_context: Arc::clone(context),
})
}
pub fn set_base_directory(self, dir: &str) -> Result<Self> {
let bytes = dir.as_bytes();
unsafe {
check_err(
self._context.as_ptr(),
sys::nix_flake_reference_parse_flags_set_base_directory(
self._context.as_ptr(),
self.inner.as_ptr(),
bytes.as_ptr().cast(),
bytes.len(),
),
)?;
}
Ok(self)
}
pub(crate) unsafe fn as_ptr(
&self,
) -> *mut sys::nix_flake_reference_parse_flags {
self.inner.as_ptr()
}
}
impl Drop for FlakeReferenceParseFlags {
fn drop(&mut self) {
unsafe {
sys::nix_flake_reference_parse_flags_free(self.inner.as_ptr());
}
}
}
unsafe impl Send for FlakeReferenceParseFlags {}
unsafe impl Sync for FlakeReferenceParseFlags {}
pub struct LockFlags {
inner: NonNull<sys::nix_flake_lock_flags>,
_context: Arc<Context>,
}
impl LockFlags {
pub fn new(
context: &Arc<Context>,
flake_settings: &FlakeSettings,
) -> Result<Self> {
let ptr = unsafe {
sys::nix_flake_lock_flags_new(context.as_ptr(), flake_settings.as_ptr())
};
let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
Ok(LockFlags {
inner,
_context: Arc::clone(context),
})
}
pub fn set_mode_check(self) -> Result<Self> {
unsafe {
check_err(
self._context.as_ptr(),
sys::nix_flake_lock_flags_set_mode_check(
self._context.as_ptr(),
self.inner.as_ptr(),
),
)?;
}
Ok(self)
}
pub fn set_mode_virtual(self) -> Result<Self> {
unsafe {
check_err(
self._context.as_ptr(),
sys::nix_flake_lock_flags_set_mode_virtual(
self._context.as_ptr(),
self.inner.as_ptr(),
),
)?;
}
Ok(self)
}
pub fn set_mode_write_as_needed(self) -> Result<Self> {
unsafe {
check_err(
self._context.as_ptr(),
sys::nix_flake_lock_flags_set_mode_write_as_needed(
self._context.as_ptr(),
self.inner.as_ptr(),
),
)?;
}
Ok(self)
}
pub fn add_input_override(
self,
input_path: &str,
flake_ref: &FlakeReference,
) -> Result<Self> {
let path_c = CString::new(input_path)?;
unsafe {
check_err(
self._context.as_ptr(),
sys::nix_flake_lock_flags_add_input_override(
self._context.as_ptr(),
self.inner.as_ptr(),
path_c.as_ptr(),
flake_ref.inner.as_ptr(),
),
)?;
}
Ok(self)
}
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_flake_lock_flags {
self.inner.as_ptr()
}
}
impl Drop for LockFlags {
fn drop(&mut self) {
unsafe {
sys::nix_flake_lock_flags_free(self.inner.as_ptr());
}
}
}
unsafe impl Send for LockFlags {}
unsafe impl Sync for LockFlags {}
unsafe extern "C" fn collect_fragment_cb(
start: *const std::os::raw::c_char,
n: std::os::raw::c_uint,
user_data: *mut std::os::raw::c_void,
) {
let result = unsafe { &mut *(user_data as *mut Option<String>) };
if !start.is_null() {
let bytes =
unsafe { std::slice::from_raw_parts(start.cast::<u8>(), n as usize) };
*result = std::str::from_utf8(bytes).ok().map(|s| s.to_owned());
}
}
pub struct FlakeReference {
inner: NonNull<sys::nix_flake_reference>,
_context: Arc<Context>,
}
impl FlakeReference {
pub fn parse(
context: &Arc<Context>,
fetch_settings: &FetchersSettings,
flake_settings: &FlakeSettings,
parse_flags: &FlakeReferenceParseFlags,
s: &str,
) -> Result<(Self, String)> {
let bytes = s.as_bytes();
let mut out_ptr: *mut sys::nix_flake_reference = std::ptr::null_mut();
let mut fragment: Option<String> = None;
let err = unsafe {
sys::nix_flake_reference_and_fragment_from_string(
context.as_ptr(),
fetch_settings.as_ptr(),
flake_settings.as_ptr(),
parse_flags.as_ptr(),
bytes.as_ptr().cast(),
bytes.len(),
&mut out_ptr as *mut *mut sys::nix_flake_reference,
Some(collect_fragment_cb),
&mut fragment as *mut Option<String> as *mut std::os::raw::c_void,
)
};
check_err(unsafe { context.as_ptr() }, err)?;
let inner = NonNull::new(out_ptr).ok_or(Error::NullPointer)?;
let frag = fragment.unwrap_or_default();
Ok((
FlakeReference {
inner,
_context: Arc::clone(context),
},
frag,
))
}
}
impl Drop for FlakeReference {
fn drop(&mut self) {
unsafe {
sys::nix_flake_reference_free(self.inner.as_ptr());
}
}
}
unsafe impl Send for FlakeReference {}
unsafe impl Sync for FlakeReference {}
pub struct LockedFlake {
inner: NonNull<sys::nix_locked_flake>,
_context: Arc<Context>,
}
impl LockedFlake {
pub fn lock(
context: &Arc<Context>,
fetch_settings: &FetchersSettings,
flake_settings: &FlakeSettings,
eval_state: &EvalState,
lock_flags: &LockFlags,
flake_ref: &FlakeReference,
) -> Result<Self> {
let ptr = unsafe {
sys::nix_flake_lock(
context.as_ptr(),
fetch_settings.as_ptr(),
flake_settings.as_ptr(),
eval_state.as_ptr(),
lock_flags.as_ptr(),
flake_ref.inner.as_ptr(),
)
};
let inner = NonNull::new(ptr).ok_or(Error::NullPointer)?;
Ok(LockedFlake {
inner,
_context: Arc::clone(context),
})
}
pub fn output_attrs<'s>(
&self,
flake_settings: &FlakeSettings,
eval_state: &'s EvalState,
) -> Result<Value<'s>> {
let ptr = unsafe {
sys::nix_locked_flake_get_output_attrs(
self._context.as_ptr(),
flake_settings.as_ptr(),
eval_state.as_ptr(),
self.inner.as_ptr(),
)
};
let inner = std::ptr::NonNull::new(ptr).ok_or(Error::NullPointer)?;
Ok(Value {
inner,
state: eval_state,
})
}
}
impl Drop for LockedFlake {
fn drop(&mut self) {
unsafe {
sys::nix_locked_flake_free(self.inner.as_ptr());
}
}
}
unsafe impl Send for LockedFlake {}
unsafe impl Sync for LockedFlake {}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use serial_test::serial;
use super::*;
use crate::{Context, EvalStateBuilder, Store};
fn make_state(ctx: &Arc<Context>) -> (Arc<Store>, EvalState) {
let store = Arc::new(Store::open(ctx, None).expect("Failed to open store"));
let flake_settings =
FlakeSettings::new(ctx).expect("Failed to create flake settings");
let state = EvalStateBuilder::new(&store)
.expect("Failed to create builder")
.with_flake_settings(&flake_settings)
.expect("Failed to apply flake settings")
.build()
.expect("Failed to build state");
(store, state)
}
#[test]
#[serial]
fn test_flake_settings_new() {
let ctx = Arc::new(Context::new().expect("Failed to create context"));
let _settings =
FlakeSettings::new(&ctx).expect("Failed to create flake settings");
}
#[test]
#[serial]
fn test_flake_settings_with_eval_state() {
let ctx = Arc::new(Context::new().expect("Failed to create context"));
make_state(&ctx);
}
#[test]
#[serial]
fn test_fetchers_settings_new() {
let ctx = Arc::new(Context::new().expect("Failed to create context"));
let _s =
FetchersSettings::new(&ctx).expect("Failed to create fetcher settings");
}
#[test]
#[serial]
fn test_flake_reference_parse_flags_new() {
let ctx = Arc::new(Context::new().expect("Failed to create context"));
let settings =
FlakeSettings::new(&ctx).expect("Failed to create flake settings");
let _f = FlakeReferenceParseFlags::new(&ctx, &settings)
.expect("Failed to create parse flags");
}
#[test]
#[serial]
fn test_flake_reference_parse_flags_set_base_directory() {
let ctx = Arc::new(Context::new().expect("Failed to create context"));
let settings =
FlakeSettings::new(&ctx).expect("Failed to create flake settings");
let _f = FlakeReferenceParseFlags::new(&ctx, &settings)
.expect("Failed to create parse flags")
.set_base_directory("/tmp")
.expect("Failed to set base directory");
}
#[test]
#[serial]
fn test_lock_flags_new() {
let ctx = Arc::new(Context::new().expect("Failed to create context"));
let settings =
FlakeSettings::new(&ctx).expect("Failed to create flake settings");
let _f =
LockFlags::new(&ctx, &settings).expect("Failed to create lock flags");
}
#[test]
#[serial]
fn test_lock_flags_set_modes() {
let ctx = Arc::new(Context::new().expect("Failed to create context"));
let settings =
FlakeSettings::new(&ctx).expect("Failed to create flake settings");
let _check = LockFlags::new(&ctx, &settings)
.expect("create")
.set_mode_check()
.expect("set_mode_check");
let _virtual = LockFlags::new(&ctx, &settings)
.expect("create")
.set_mode_virtual()
.expect("set_mode_virtual");
let _write = LockFlags::new(&ctx, &settings)
.expect("create")
.set_mode_write_as_needed()
.expect("set_mode_write_as_needed");
}
}