refact: Add Context::check_one_call(f)
This makes it easier to stick to a regular pattern and not forget to check the context afterwards. (cherry picked from commit 966ef3769a23075f5d660a48cf5a7307df925dbf)
This commit is contained in:
parent
c775792b63
commit
61efb9b79f
2 changed files with 110 additions and 93 deletions
|
|
@ -17,12 +17,9 @@ lazy_static! {
|
|||
unsafe {
|
||||
raw::GC_allow_register_threads();
|
||||
}
|
||||
let context: Context = Context::new();
|
||||
unsafe {
|
||||
raw::libexpr_init(context.ptr());
|
||||
}
|
||||
context.check_err()?;
|
||||
Ok(())
|
||||
Context::new().check_one_call(|ctx_ptr| unsafe {
|
||||
raw::libexpr_init(ctx_ptr);
|
||||
})
|
||||
};
|
||||
}
|
||||
pub fn init() -> Result<()> {
|
||||
|
|
@ -70,14 +67,13 @@ impl EvalState {
|
|||
|
||||
init()?;
|
||||
|
||||
let eval_state =
|
||||
unsafe { raw::state_create(context.ptr(), lookup_path.as_mut_ptr(), store.raw_ptr()) };
|
||||
context.check_err()?;
|
||||
if eval_state.is_null() {
|
||||
panic!("nix_state_create returned a null pointer without an error");
|
||||
}
|
||||
let eval_state = context.check_one_call(|ctx_ptr| unsafe {
|
||||
raw::state_create(ctx_ptr, lookup_path.as_mut_ptr(), store.raw_ptr())
|
||||
})?;
|
||||
Ok(EvalState {
|
||||
eval_state: NonNull::new(eval_state).unwrap(),
|
||||
eval_state: NonNull::new(eval_state).unwrap_or_else(|| {
|
||||
panic!("nix_state_create returned a null pointer without an error")
|
||||
}),
|
||||
store,
|
||||
context,
|
||||
})
|
||||
|
|
@ -112,32 +108,35 @@ impl EvalState {
|
|||
CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?;
|
||||
let path_ptr =
|
||||
CString::new(path).with_context(|| "eval_from_string: path contains null byte")?;
|
||||
let value = self.new_value_uninitialized();
|
||||
unsafe {
|
||||
let ctx_ptr = self.context.ptr();
|
||||
raw::expr_eval_from_string(
|
||||
ctx_ptr,
|
||||
self.raw_ptr(),
|
||||
expr_ptr.as_ptr(),
|
||||
path_ptr.as_ptr(),
|
||||
value.raw_ptr(),
|
||||
);
|
||||
};
|
||||
self.context.check_err()?;
|
||||
Ok(value)
|
||||
let value = self.new_value_uninitialized()?;
|
||||
self.context.check_one_call(|ctx_ptr| {
|
||||
raw::expr_eval_from_string(
|
||||
ctx_ptr,
|
||||
self.raw_ptr(),
|
||||
expr_ptr.as_ptr(),
|
||||
path_ptr.as_ptr(),
|
||||
value.raw_ptr(),
|
||||
)
|
||||
})?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
/// Try turn any Value into a Value that isn't a Thunk.
|
||||
pub fn force(&self, v: &Value) -> Result<()> {
|
||||
unsafe {
|
||||
raw::value_force(self.context.ptr(), self.raw_ptr(), v.raw_ptr());
|
||||
self.context.check_one_call(|ctx_ptr| {
|
||||
raw::value_force(ctx_ptr, self.raw_ptr(), v.raw_ptr());
|
||||
})?;
|
||||
}
|
||||
self.context.check_err()
|
||||
Ok(())
|
||||
}
|
||||
pub fn value_type_unforced(&self, value: &Value) -> Option<ValueType> {
|
||||
let r = unsafe { raw::get_type(self.context.ptr(), value.raw_ptr()) };
|
||||
let r = self
|
||||
.context
|
||||
.check_one_call(|ctx_ptr| unsafe { raw::get_type(ctx_ptr, value.raw_ptr()) });
|
||||
// .unwrap(): no reason for this to fail, as it does not evaluate
|
||||
self.context.check_err().unwrap();
|
||||
ValueType::from_raw(r)
|
||||
ValueType::from_raw(r.unwrap())
|
||||
}
|
||||
pub fn value_type(&self, value: &Value) -> Result<ValueType> {
|
||||
match self.value_type_unforced(value) {
|
||||
|
|
@ -158,8 +157,8 @@ impl EvalState {
|
|||
if t != ValueType::Int {
|
||||
bail!("expected an int, but got a {:?}", t);
|
||||
}
|
||||
let i = unsafe { raw::get_int(self.context.ptr(), v.raw_ptr()) };
|
||||
Ok(i)
|
||||
self.context
|
||||
.check_one_call(|ctx_ptr| unsafe { raw::get_int(ctx_ptr, v.raw_ptr()) })
|
||||
}
|
||||
/// Evaluate, and require that the value is an attrset.
|
||||
/// Returns a list of the keys in the attrset.
|
||||
|
|
@ -168,28 +167,25 @@ impl EvalState {
|
|||
if t != ValueType::AttrSet {
|
||||
bail!("expected an attrset, but got a {:?}", t);
|
||||
}
|
||||
let n = unsafe { raw::get_attrs_size(self.context.ptr(), v.raw_ptr()) as usize };
|
||||
self.context.check_err()?;
|
||||
let n = self.context.check_one_call(|ctx_ptr| unsafe {
|
||||
raw::get_attrs_size(ctx_ptr, v.raw_ptr()) as usize
|
||||
})?;
|
||||
let mut attrs = Vec::with_capacity(n);
|
||||
unsafe {
|
||||
for i in 0..n {
|
||||
let cstr_ptr: *const i8 = raw::get_attr_name_byidx(
|
||||
self.context.ptr(),
|
||||
v.raw_ptr(),
|
||||
self.raw_ptr(),
|
||||
i as c_uint,
|
||||
);
|
||||
self.context.check_err()?;
|
||||
let cstr = std::ffi::CStr::from_ptr(cstr_ptr);
|
||||
let s = cstr.to_str().map_err(|e| {
|
||||
anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e)
|
||||
})?;
|
||||
attrs.insert(i, s.to_owned());
|
||||
}
|
||||
for i in 0..n {
|
||||
let cstr_ptr: *const i8 = self.context.check_one_call(|ctx_ptr| unsafe {
|
||||
raw::get_attr_name_byidx(ctx_ptr, v.raw_ptr(), self.raw_ptr(), i as c_uint)
|
||||
})?;
|
||||
let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) };
|
||||
let s = cstr
|
||||
.to_str()
|
||||
.map_err(|e| anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e))?;
|
||||
attrs.insert(i, s.to_owned());
|
||||
}
|
||||
Ok(attrs)
|
||||
}
|
||||
|
||||
// TODO: make this the main implementation and move the error handling to require_attrs_select_opt
|
||||
// that gets rid of the odd self.context.check_err() usage that relies on the context not being reset
|
||||
pub fn require_attrs_select(&self, v: &Value, attr_name: &str) -> Result<Value> {
|
||||
let r = self.require_attrs_select_opt(v, attr_name)?;
|
||||
match r {
|
||||
|
|
@ -231,21 +227,21 @@ impl EvalState {
|
|||
pub fn new_value_str(&self, s: &str) -> Result<Value> {
|
||||
let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?;
|
||||
let v = unsafe {
|
||||
let value = self.new_value_uninitialized();
|
||||
raw::init_string(self.context.ptr(), value.raw_ptr(), s.as_ptr());
|
||||
let value = self.new_value_uninitialized()?;
|
||||
self.context
|
||||
.check_one_call(|ctx_ptr| raw::init_string(ctx_ptr, value.raw_ptr(), s.as_ptr()))?;
|
||||
value
|
||||
};
|
||||
self.context.check_err()?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn new_value_int(&self, i: Int) -> Result<Value> {
|
||||
let v = unsafe {
|
||||
let value = self.new_value_uninitialized();
|
||||
raw::init_int(self.context.ptr(), value.raw_ptr(), i);
|
||||
let value = self.new_value_uninitialized()?;
|
||||
self.context
|
||||
.check_one_call(|ctx_ptr| raw::init_int(ctx_ptr, value.raw_ptr(), i))?;
|
||||
value
|
||||
};
|
||||
self.context.check_err()?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
|
|
@ -253,14 +249,15 @@ impl EvalState {
|
|||
fn get_string(&self, value: &Value) -> Result<String> {
|
||||
let mut r = result_string_init!();
|
||||
unsafe {
|
||||
raw::get_string(
|
||||
self.context.ptr(),
|
||||
value.raw_ptr(),
|
||||
Some(callback_get_result_string),
|
||||
callback_get_result_string_data(&mut r),
|
||||
)
|
||||
self.context.check_one_call(|ctx_ptr| {
|
||||
raw::get_string(
|
||||
ctx_ptr,
|
||||
value.raw_ptr(),
|
||||
Some(callback_get_result_string),
|
||||
callback_get_result_string_data(&mut r),
|
||||
)
|
||||
})?;
|
||||
};
|
||||
self.context.check_err()?;
|
||||
r
|
||||
}
|
||||
/// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty
|
||||
|
|
@ -281,15 +278,14 @@ impl EvalState {
|
|||
bail!("expected a string, but got a {:?}", t);
|
||||
}
|
||||
|
||||
let rs = unsafe {
|
||||
let rs = self.context.check_one_call(|ctx_ptr| unsafe {
|
||||
raw::string_realise(
|
||||
self.context.ptr(),
|
||||
ctx_ptr,
|
||||
self.raw_ptr(),
|
||||
value.raw_ptr(),
|
||||
is_import_from_derivation,
|
||||
)
|
||||
};
|
||||
self.context.check_err()?;
|
||||
})?;
|
||||
|
||||
let s = unsafe {
|
||||
let start = raw::realised_string_get_buffer_start(rs) as *const u8;
|
||||
|
|
@ -321,41 +317,37 @@ impl EvalState {
|
|||
///
|
||||
/// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`].
|
||||
pub fn call(&self, f: Value, a: Value) -> Result<Value> {
|
||||
let v = unsafe {
|
||||
let value = self.new_value_uninitialized();
|
||||
raw::value_call(
|
||||
self.context.ptr(),
|
||||
self.raw_ptr(),
|
||||
f.raw_ptr(),
|
||||
a.raw_ptr(),
|
||||
value.raw_ptr(),
|
||||
);
|
||||
value
|
||||
};
|
||||
self.context.check_err()?;
|
||||
Ok(v)
|
||||
unsafe {
|
||||
let value = self.new_value_uninitialized()?;
|
||||
self.context.check_one_call(|ctx_ptr| {
|
||||
raw::value_call(
|
||||
ctx_ptr,
|
||||
self.raw_ptr(),
|
||||
f.raw_ptr(),
|
||||
a.raw_ptr(),
|
||||
value.raw_ptr(),
|
||||
)
|
||||
})?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a function to an argument, but don't evaluate the result just yet.
|
||||
///
|
||||
/// For an eager version, see [`call`][`EvalState::call`].
|
||||
pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result<Value> {
|
||||
let value = self.new_value_uninitialized();
|
||||
unsafe {
|
||||
raw::init_apply(
|
||||
self.context.ptr(),
|
||||
value.raw_ptr(),
|
||||
f.raw_ptr(),
|
||||
a.raw_ptr(),
|
||||
);
|
||||
};
|
||||
self.context.check_err()?;
|
||||
Ok(value)
|
||||
let value = self.new_value_uninitialized()?;
|
||||
self.context.check_one_call(|ctx_ptr| unsafe {
|
||||
raw::init_apply(ctx_ptr, value.raw_ptr(), f.raw_ptr(), a.raw_ptr());
|
||||
value
|
||||
})
|
||||
}
|
||||
|
||||
fn new_value_uninitialized(&self) -> Value {
|
||||
let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) };
|
||||
Value::new(value)
|
||||
fn new_value_uninitialized(&self) -> Result<Value> {
|
||||
let value = self
|
||||
.context
|
||||
.check_one_call(|ctx_ptr| unsafe { raw::alloc_value(ctx_ptr, self.raw_ptr()) })?;
|
||||
Ok(Value::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,31 @@ impl Context {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_err_and_clear(&self) -> Result<()> {
|
||||
let r = self.check_err();
|
||||
if r.is_err() {
|
||||
unsafe {
|
||||
// TODO (https://github.com/NixOS/nix/pull/10910): raw::err_clear
|
||||
raw::set_err_msg(
|
||||
self.inner.as_ptr(),
|
||||
raw::NIX_OK.try_into().unwrap(),
|
||||
b"\0".as_ptr() as *const i8,
|
||||
);
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// Run the function, and check the error, then reset the error.
|
||||
/// Make at most one call to a Nix function in `f`.
|
||||
/// Do not use if the context isn't fresh or cleared (e.g. with `check_err_and_clear`).
|
||||
pub fn check_one_call<T, F: FnOnce(*mut raw::c_context) -> T>(&self, f: F) -> Result<T> {
|
||||
let t = f(self.ptr());
|
||||
self.check_err_and_clear()?;
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
/// NIX_ERR_KEY is returned when e.g. an attribute is missing. Return true if the error is of this type.
|
||||
pub fn is_key_error(&self) -> bool {
|
||||
unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue