nix_bindings_util/
context.rs1use anyhow::{bail, Result};
2use nix_bindings_util_sys as raw;
3use std::ptr::null_mut;
4use std::ptr::NonNull;
5
6pub struct Context {
10 inner: NonNull<raw::c_context>,
11}
12
13impl Default for Context {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl Context {
20 pub fn new() -> Self {
21 let ctx = unsafe { raw::c_context_create() };
22 if ctx.is_null() {
23 panic!("nix_c_context_create returned a null pointer");
26 }
27 Context {
28 inner: NonNull::new(ctx).unwrap(),
29 }
30 }
31
32 pub fn ptr(&mut self) -> *mut raw::c_context {
36 self.inner.as_ptr()
37 }
38
39 pub fn check_err(&self) -> Result<()> {
43 let err = unsafe { raw::err_code(self.inner.as_ptr()) };
44 if err != raw::err_NIX_OK {
45 let msgp = unsafe { raw::err_msg(null_mut(), self.inner.as_ptr(), null_mut()) };
47 let msg: &str = unsafe { core::ffi::CStr::from_ptr(msgp).to_str()? };
49 bail!("{}", msg);
50 }
51 Ok(())
52 }
53
54 pub fn clear(&mut self) {
55 unsafe {
56 raw::set_err_msg(self.inner.as_ptr(), raw::err_NIX_OK, c"".as_ptr());
57 }
58 }
59
60 pub fn check_err_and_clear(&mut self) -> Result<()> {
61 let r = self.check_err();
62 if r.is_err() {
63 self.clear();
64 }
65 r
66 }
67
68 pub fn check_one_call_or_key_none<T, F: FnOnce(*mut raw::c_context) -> T>(
69 &mut self,
70 f: F,
71 ) -> Result<Option<T>> {
72 let t = f(self.ptr());
73 if unsafe { raw::err_code(self.inner.as_ptr()) == raw::err_NIX_ERR_KEY } {
74 self.clear();
75 return Ok(None);
76 }
77 self.check_err_and_clear()?;
78 Ok(Some(t))
79 }
80}
81
82impl Drop for Context {
83 fn drop(&mut self) {
84 unsafe {
85 raw::c_context_free(self.inner.as_ptr());
86 }
87 }
88}
89
90#[macro_export]
91macro_rules! check_call {
92 ($($f:ident)::+($ctx:expr $(, $arg:expr)*)) => {
93 {
94 let ctx : &mut $crate::context::Context = $ctx;
95 let ret = $($f)::*(ctx.ptr() $(, $arg)*);
96 match ctx.check_err() {
97 Ok(_) => Ok(ret),
98 Err(e) => {
99 ctx.clear();
100 Err(e)
101 }
102 }
103 }
104 }
105}
106
107pub use check_call;
108
109#[macro_export]
111macro_rules! check_call_opt_key {
112 ($($f:ident)::+($ctx:expr, $($arg:expr),*)) => {
113 {
114 let ctx : &mut $crate::context::Context = $ctx;
115 let ret = $($f)::*(ctx.ptr(), $($arg,)*);
116 if unsafe { $crate::raw_sys::err_code(ctx.ptr()) == $crate::raw_sys::err_NIX_ERR_KEY } {
117 ctx.clear();
118 return Ok(None);
119 }
120 match ctx.check_err() {
121 Ok(_) => Ok(Some(ret)),
122 Err(e) => {
123 ctx.clear();
124 Err(e)
125 }
126 }
127 }
128 }
129}
130
131pub use check_call_opt_key;
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn context_new_and_drop() {
139 let _c = Context::new();
141 }
142
143 fn set_dummy_err(ctx_ptr: *mut raw::c_context) {
144 unsafe {
145 raw::set_err_msg(
146 ctx_ptr,
147 raw::err_NIX_ERR_UNKNOWN,
148 c"dummy error message".as_ptr(),
149 );
150 }
151 }
152
153 #[test]
154 fn check_call_dynamic_context() {
155 let r = check_call!(set_dummy_err(&mut Context::new()));
156 assert!(r.is_err());
157 assert_eq!(r.unwrap_err().to_string(), "dummy error message");
158 }
159}