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 {
|
unsafe {
|
||||||
raw::GC_allow_register_threads();
|
raw::GC_allow_register_threads();
|
||||||
}
|
}
|
||||||
let context: Context = Context::new();
|
Context::new().check_one_call(|ctx_ptr| unsafe {
|
||||||
unsafe {
|
raw::libexpr_init(ctx_ptr);
|
||||||
raw::libexpr_init(context.ptr());
|
})
|
||||||
}
|
|
||||||
context.check_err()?;
|
|
||||||
Ok(())
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn init() -> Result<()> {
|
pub fn init() -> Result<()> {
|
||||||
|
|
@ -70,14 +67,13 @@ impl EvalState {
|
||||||
|
|
||||||
init()?;
|
init()?;
|
||||||
|
|
||||||
let eval_state =
|
let eval_state = context.check_one_call(|ctx_ptr| unsafe {
|
||||||
unsafe { raw::state_create(context.ptr(), lookup_path.as_mut_ptr(), store.raw_ptr()) };
|
raw::state_create(ctx_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");
|
|
||||||
}
|
|
||||||
Ok(EvalState {
|
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,
|
store,
|
||||||
context,
|
context,
|
||||||
})
|
})
|
||||||
|
|
@ -112,32 +108,35 @@ impl EvalState {
|
||||||
CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?;
|
CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?;
|
||||||
let path_ptr =
|
let path_ptr =
|
||||||
CString::new(path).with_context(|| "eval_from_string: path contains null byte")?;
|
CString::new(path).with_context(|| "eval_from_string: path contains null byte")?;
|
||||||
let value = self.new_value_uninitialized();
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ctx_ptr = self.context.ptr();
|
let value = self.new_value_uninitialized()?;
|
||||||
raw::expr_eval_from_string(
|
self.context.check_one_call(|ctx_ptr| {
|
||||||
ctx_ptr,
|
raw::expr_eval_from_string(
|
||||||
self.raw_ptr(),
|
ctx_ptr,
|
||||||
expr_ptr.as_ptr(),
|
self.raw_ptr(),
|
||||||
path_ptr.as_ptr(),
|
expr_ptr.as_ptr(),
|
||||||
value.raw_ptr(),
|
path_ptr.as_ptr(),
|
||||||
);
|
value.raw_ptr(),
|
||||||
};
|
)
|
||||||
self.context.check_err()?;
|
})?;
|
||||||
Ok(value)
|
Ok(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Try turn any Value into a Value that isn't a Thunk.
|
/// Try turn any Value into a Value that isn't a Thunk.
|
||||||
pub fn force(&self, v: &Value) -> Result<()> {
|
pub fn force(&self, v: &Value) -> Result<()> {
|
||||||
unsafe {
|
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> {
|
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
|
// .unwrap(): no reason for this to fail, as it does not evaluate
|
||||||
self.context.check_err().unwrap();
|
ValueType::from_raw(r.unwrap())
|
||||||
ValueType::from_raw(r)
|
|
||||||
}
|
}
|
||||||
pub fn value_type(&self, value: &Value) -> Result<ValueType> {
|
pub fn value_type(&self, value: &Value) -> Result<ValueType> {
|
||||||
match self.value_type_unforced(value) {
|
match self.value_type_unforced(value) {
|
||||||
|
|
@ -158,8 +157,8 @@ impl EvalState {
|
||||||
if t != ValueType::Int {
|
if t != ValueType::Int {
|
||||||
bail!("expected an int, but got a {:?}", t);
|
bail!("expected an int, but got a {:?}", t);
|
||||||
}
|
}
|
||||||
let i = unsafe { raw::get_int(self.context.ptr(), v.raw_ptr()) };
|
self.context
|
||||||
Ok(i)
|
.check_one_call(|ctx_ptr| unsafe { raw::get_int(ctx_ptr, v.raw_ptr()) })
|
||||||
}
|
}
|
||||||
/// Evaluate, and require that the value is an attrset.
|
/// Evaluate, and require that the value is an attrset.
|
||||||
/// Returns a list of the keys in the attrset.
|
/// Returns a list of the keys in the attrset.
|
||||||
|
|
@ -168,28 +167,25 @@ impl EvalState {
|
||||||
if t != ValueType::AttrSet {
|
if t != ValueType::AttrSet {
|
||||||
bail!("expected an attrset, but got a {:?}", t);
|
bail!("expected an attrset, but got a {:?}", t);
|
||||||
}
|
}
|
||||||
let n = unsafe { raw::get_attrs_size(self.context.ptr(), v.raw_ptr()) as usize };
|
let n = self.context.check_one_call(|ctx_ptr| unsafe {
|
||||||
self.context.check_err()?;
|
raw::get_attrs_size(ctx_ptr, v.raw_ptr()) as usize
|
||||||
|
})?;
|
||||||
let mut attrs = Vec::with_capacity(n);
|
let mut attrs = Vec::with_capacity(n);
|
||||||
unsafe {
|
for i in 0..n {
|
||||||
for i in 0..n {
|
let cstr_ptr: *const i8 = self.context.check_one_call(|ctx_ptr| unsafe {
|
||||||
let cstr_ptr: *const i8 = raw::get_attr_name_byidx(
|
raw::get_attr_name_byidx(ctx_ptr, v.raw_ptr(), self.raw_ptr(), i as c_uint)
|
||||||
self.context.ptr(),
|
})?;
|
||||||
v.raw_ptr(),
|
let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) };
|
||||||
self.raw_ptr(),
|
let s = cstr
|
||||||
i as c_uint,
|
.to_str()
|
||||||
);
|
.map_err(|e| anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e))?;
|
||||||
self.context.check_err()?;
|
attrs.insert(i, s.to_owned());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(attrs)
|
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> {
|
pub fn require_attrs_select(&self, v: &Value, attr_name: &str) -> Result<Value> {
|
||||||
let r = self.require_attrs_select_opt(v, attr_name)?;
|
let r = self.require_attrs_select_opt(v, attr_name)?;
|
||||||
match r {
|
match r {
|
||||||
|
|
@ -231,21 +227,21 @@ impl EvalState {
|
||||||
pub fn new_value_str(&self, s: &str) -> Result<Value> {
|
pub fn new_value_str(&self, s: &str) -> Result<Value> {
|
||||||
let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?;
|
let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?;
|
||||||
let v = unsafe {
|
let v = unsafe {
|
||||||
let value = self.new_value_uninitialized();
|
let value = self.new_value_uninitialized()?;
|
||||||
raw::init_string(self.context.ptr(), value.raw_ptr(), s.as_ptr());
|
self.context
|
||||||
|
.check_one_call(|ctx_ptr| raw::init_string(ctx_ptr, value.raw_ptr(), s.as_ptr()))?;
|
||||||
value
|
value
|
||||||
};
|
};
|
||||||
self.context.check_err()?;
|
|
||||||
Ok(v)
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_value_int(&self, i: Int) -> Result<Value> {
|
pub fn new_value_int(&self, i: Int) -> Result<Value> {
|
||||||
let v = unsafe {
|
let v = unsafe {
|
||||||
let value = self.new_value_uninitialized();
|
let value = self.new_value_uninitialized()?;
|
||||||
raw::init_int(self.context.ptr(), value.raw_ptr(), i);
|
self.context
|
||||||
|
.check_one_call(|ctx_ptr| raw::init_int(ctx_ptr, value.raw_ptr(), i))?;
|
||||||
value
|
value
|
||||||
};
|
};
|
||||||
self.context.check_err()?;
|
|
||||||
Ok(v)
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,14 +249,15 @@ impl EvalState {
|
||||||
fn get_string(&self, value: &Value) -> Result<String> {
|
fn get_string(&self, value: &Value) -> Result<String> {
|
||||||
let mut r = result_string_init!();
|
let mut r = result_string_init!();
|
||||||
unsafe {
|
unsafe {
|
||||||
raw::get_string(
|
self.context.check_one_call(|ctx_ptr| {
|
||||||
self.context.ptr(),
|
raw::get_string(
|
||||||
value.raw_ptr(),
|
ctx_ptr,
|
||||||
Some(callback_get_result_string),
|
value.raw_ptr(),
|
||||||
callback_get_result_string_data(&mut r),
|
Some(callback_get_result_string),
|
||||||
)
|
callback_get_result_string_data(&mut r),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
};
|
};
|
||||||
self.context.check_err()?;
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
/// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty
|
/// 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);
|
bail!("expected a string, but got a {:?}", t);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rs = unsafe {
|
let rs = self.context.check_one_call(|ctx_ptr| unsafe {
|
||||||
raw::string_realise(
|
raw::string_realise(
|
||||||
self.context.ptr(),
|
ctx_ptr,
|
||||||
self.raw_ptr(),
|
self.raw_ptr(),
|
||||||
value.raw_ptr(),
|
value.raw_ptr(),
|
||||||
is_import_from_derivation,
|
is_import_from_derivation,
|
||||||
)
|
)
|
||||||
};
|
})?;
|
||||||
self.context.check_err()?;
|
|
||||||
|
|
||||||
let s = unsafe {
|
let s = unsafe {
|
||||||
let start = raw::realised_string_get_buffer_start(rs) as *const u8;
|
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`].
|
/// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`].
|
||||||
pub fn call(&self, f: Value, a: Value) -> Result<Value> {
|
pub fn call(&self, f: Value, a: Value) -> Result<Value> {
|
||||||
let v = unsafe {
|
unsafe {
|
||||||
let value = self.new_value_uninitialized();
|
let value = self.new_value_uninitialized()?;
|
||||||
raw::value_call(
|
self.context.check_one_call(|ctx_ptr| {
|
||||||
self.context.ptr(),
|
raw::value_call(
|
||||||
self.raw_ptr(),
|
ctx_ptr,
|
||||||
f.raw_ptr(),
|
self.raw_ptr(),
|
||||||
a.raw_ptr(),
|
f.raw_ptr(),
|
||||||
value.raw_ptr(),
|
a.raw_ptr(),
|
||||||
);
|
value.raw_ptr(),
|
||||||
value
|
)
|
||||||
};
|
})?;
|
||||||
self.context.check_err()?;
|
Ok(value)
|
||||||
Ok(v)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a function to an argument, but don't evaluate the result just yet.
|
/// Apply a function to an argument, but don't evaluate the result just yet.
|
||||||
///
|
///
|
||||||
/// For an eager version, see [`call`][`EvalState::call`].
|
/// For an eager version, see [`call`][`EvalState::call`].
|
||||||
pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result<Value> {
|
pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result<Value> {
|
||||||
let value = self.new_value_uninitialized();
|
let value = self.new_value_uninitialized()?;
|
||||||
unsafe {
|
self.context.check_one_call(|ctx_ptr| unsafe {
|
||||||
raw::init_apply(
|
raw::init_apply(ctx_ptr, value.raw_ptr(), f.raw_ptr(), a.raw_ptr());
|
||||||
self.context.ptr(),
|
value
|
||||||
value.raw_ptr(),
|
})
|
||||||
f.raw_ptr(),
|
|
||||||
a.raw_ptr(),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
self.context.check_err()?;
|
|
||||||
Ok(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_value_uninitialized(&self) -> Value {
|
fn new_value_uninitialized(&self) -> Result<Value> {
|
||||||
let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) };
|
let value = self
|
||||||
Value::new(value)
|
.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(())
|
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.
|
/// 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 {
|
pub fn is_key_error(&self) -> bool {
|
||||||
unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY }
|
unsafe { raw::err_code(self.inner.as_ptr()) == raw::NIX_ERR_KEY }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue