use std::ffi::CStr;
use crate::{Error, Result, ValueType, check_err, sys};
pub(crate) mod sealed {
use super::sys;
pub trait NixValueRaw {
fn raw_ctx(&self) -> *mut sys::nix_c_context;
fn raw_state(&self) -> *mut sys::EvalState;
fn raw_inner(&self) -> *mut sys::nix_value;
}
}
pub(crate) use sealed::NixValueRaw;
pub trait NixValueOps: sealed::NixValueRaw {
fn value_type(&self) -> ValueType {
let c_type = unsafe { sys::nix_get_type(self.raw_ctx(), self.raw_inner()) };
ValueType::from_c(c_type)
}
fn force(&self) -> Result<()> {
unsafe {
check_err(
self.raw_ctx(),
sys::nix_value_force(
self.raw_ctx(),
self.raw_state(),
self.raw_inner(),
),
)
}
}
fn as_int(&self) -> Result<i64> {
self.force()?;
if self.value_type() != ValueType::Int {
return Err(Error::InvalidType {
expected: "int",
actual: self.value_type().to_string(),
});
}
Ok(unsafe { sys::nix_get_int(self.raw_ctx(), self.raw_inner()) })
}
fn as_float(&self) -> Result<f64> {
self.force()?;
if self.value_type() != ValueType::Float {
return Err(Error::InvalidType {
expected: "float",
actual: self.value_type().to_string(),
});
}
Ok(unsafe { sys::nix_get_float(self.raw_ctx(), self.raw_inner()) })
}
fn as_bool(&self) -> Result<bool> {
self.force()?;
if self.value_type() != ValueType::Bool {
return Err(Error::InvalidType {
expected: "bool",
actual: self.value_type().to_string(),
});
}
Ok(unsafe { sys::nix_get_bool(self.raw_ctx(), self.raw_inner()) })
}
fn as_string(&self) -> Result<String> {
self.force()?;
if self.value_type() != ValueType::String {
return Err(Error::InvalidType {
expected: "string",
actual: self.value_type().to_string(),
});
}
let realised_str = unsafe {
sys::nix_string_realise(
self.raw_ctx(),
self.raw_state(),
self.raw_inner(),
false,
)
};
if realised_str.is_null() {
return Err(Error::NullPointer);
}
let buffer_start =
unsafe { sys::nix_realised_string_get_buffer_start(realised_str) };
let buffer_size =
unsafe { sys::nix_realised_string_get_buffer_size(realised_str) };
if buffer_start.is_null() {
unsafe { sys::nix_realised_string_free(realised_str) };
return Err(Error::NullPointer);
}
let bytes = unsafe {
std::slice::from_raw_parts(buffer_start.cast::<u8>(), buffer_size)
};
let s = std::str::from_utf8(bytes)
.map_err(|_| Error::Unknown("Invalid UTF-8 in string".into()))?
.to_owned();
unsafe { sys::nix_realised_string_free(realised_str) };
Ok(s)
}
fn as_path(&self) -> Result<String> {
self.force()?;
if self.value_type() != ValueType::Path {
return Err(Error::InvalidType {
expected: "path",
actual: self.value_type().to_string(),
});
}
let raw =
unsafe { sys::nix_get_path_string(self.raw_ctx(), self.raw_inner()) };
if raw.is_null() {
return Err(Error::NullPointer);
}
let cstr = unsafe { CStr::from_ptr(raw) };
cstr
.to_str()
.map(str::to_owned)
.map_err(|_| Error::Unknown("Invalid UTF-8 in path".into()))
}
}
impl<T: sealed::NixValueRaw> NixValueOps for T {}