2024-02-16 09:59:51 +01:00
use anyhow ::{ bail , Result } ;
2026-01-12 19:56:04 +01:00
use nix_bindings_util_sys as raw ;
2024-02-16 09:59:51 +01:00
use std ::ptr ::null_mut ;
use std ::ptr ::NonNull ;
2026-01-12 19:56:04 +01:00
/// A context for error handling, when interacting directly with the generated bindings for the C API in [nix_bindings_util_sys].
2024-06-27 18:26:05 +02:00
///
/// The `nix-store` and `nix-expr` libraries that consume this type internally store a private context in their `EvalState` and `Store` structs to avoid allocating a new context for each operation. The state of a context is irrelevant when used correctly (e.g. with [check_call!]), so it's safe to reuse, and safe to allocate more contexts in methods such as [Clone::clone].
2024-02-16 09:59:51 +01:00
pub struct Context {
2024-05-09 12:29:32 -04:00
inner : NonNull < raw ::c_context > ,
2024-02-16 09:59:51 +01:00
}
2025-12-15 19:48:30 -05:00
impl Default for Context {
fn default ( ) -> Self {
Self ::new ( )
}
}
2024-02-16 09:59:51 +01:00
impl Context {
pub fn new ( ) -> Self {
2024-05-09 12:29:32 -04:00
let ctx = unsafe { raw ::c_context_create ( ) } ;
2024-02-16 09:59:51 +01:00
if ctx . is_null ( ) {
2024-05-09 12:41:34 -04:00
// We've failed to allocate a (relatively small) Context struct.
// We're almost certainly going to crash anyways.
2024-02-16 09:59:51 +01:00
panic! ( " nix_c_context_create returned a null pointer " ) ;
}
2024-11-27 10:26:39 +01:00
Context {
2024-02-16 09:59:51 +01:00
inner : NonNull ::new ( ctx ) . unwrap ( ) ,
2024-11-27 10:26:39 +01:00
}
2024-02-16 09:59:51 +01:00
}
2024-06-14 19:58:44 +02:00
2024-06-15 12:45:23 +02:00
/// Access the C context pointer.
2024-06-14 19:58:44 +02:00
///
2024-06-15 12:45:23 +02:00
/// We recommend to use `check_call!` if possible.
pub fn ptr ( & mut self ) -> * mut raw ::c_context {
2024-02-16 09:59:51 +01:00
self . inner . as_ptr ( )
}
2024-06-14 19:58:44 +02:00
/// Check the error code and return an error if it's not `NIX_OK`.
///
2024-06-15 12:45:23 +02:00
/// We recommend to use `check_call!` if possible.
pub fn check_err ( & self ) -> Result < ( ) > {
2024-05-09 12:29:32 -04:00
let err = unsafe { raw ::err_code ( self . inner . as_ptr ( ) ) } ;
2024-11-27 10:26:39 +01:00
if err ! = raw ::err_NIX_OK {
2024-05-09 12:41:34 -04:00
// msgp is a borrowed pointer (pointing into the context), so we don't need to free it
2024-05-09 12:29:32 -04:00
let msgp = unsafe { raw ::err_msg ( null_mut ( ) , self . inner . as_ptr ( ) , null_mut ( ) ) } ;
2024-02-16 09:59:51 +01:00
// Turn the i8 pointer into a Rust string by copying
let msg : & str = unsafe { core ::ffi ::CStr ::from_ptr ( msgp ) . to_str ( ) ? } ;
bail! ( " {} " , msg ) ;
}
Ok ( ( ) )
}
2024-06-14 17:28:50 +02:00
2024-06-15 12:40:45 +02:00
pub fn clear ( & mut self ) {
2024-06-14 18:21:13 +02:00
unsafe {
2025-12-15 19:48:30 -05:00
raw ::set_err_msg ( self . inner . as_ptr ( ) , raw ::err_NIX_OK , c " " . as_ptr ( ) ) ;
2024-06-14 18:21:13 +02:00
}
}
2024-06-15 12:40:45 +02:00
pub fn check_err_and_clear ( & mut self ) -> Result < ( ) > {
2024-06-14 17:28:50 +02:00
let r = self . check_err ( ) ;
if r . is_err ( ) {
2024-06-14 18:21:13 +02:00
self . clear ( ) ;
2024-06-14 17:28:50 +02:00
}
r
}
2024-06-14 18:21:13 +02:00
pub fn check_one_call_or_key_none < T , F : FnOnce ( * mut raw ::c_context ) -> T > (
2024-06-15 12:40:45 +02:00
& mut self ,
2024-06-14 18:21:13 +02:00
f : F ,
) -> Result < Option < T > > {
let t = f ( self . ptr ( ) ) ;
2024-11-26 13:58:04 +01:00
if unsafe { raw ::err_code ( self . inner . as_ptr ( ) ) = = raw ::err_NIX_ERR_KEY } {
2024-06-14 18:21:13 +02:00
self . clear ( ) ;
return Ok ( None ) ;
}
self . check_err_and_clear ( ) ? ;
Ok ( Some ( t ) )
}
2024-02-16 09:59:51 +01:00
}
impl Drop for Context {
fn drop ( & mut self ) {
unsafe {
2024-05-09 12:29:32 -04:00
raw ::c_context_free ( self . inner . as_ptr ( ) ) ;
2024-02-16 09:59:51 +01:00
}
}
}
2024-06-15 12:09:11 +02:00
#[ macro_export ]
macro_rules ! check_call {
2024-07-02 16:35:32 -04:00
( $( $f :ident ) ::+ ( $ctx :expr $(, $arg :expr ) * ) ) = > {
2024-06-15 12:09:11 +02:00
{
2024-06-15 14:34:30 +02:00
let ctx : & mut $crate ::context ::Context = $ctx ;
2024-07-02 16:35:32 -04:00
let ret = $( $f ) ::* ( ctx . ptr ( ) $(, $arg ) * ) ;
2024-06-15 14:34:30 +02:00
match ctx . check_err ( ) {
2024-06-15 12:09:11 +02:00
Ok ( _ ) = > Ok ( ret ) ,
Err ( e ) = > {
2024-06-15 14:34:30 +02:00
ctx . clear ( ) ;
2024-06-15 12:09:11 +02:00
Err ( e )
}
}
}
}
}
pub use check_call ;
2024-06-15 12:45:23 +02:00
// TODO: Generalize this macro to work with any error code or any error handling logic
#[ macro_export ]
macro_rules ! check_call_opt_key {
2024-07-02 16:44:04 -04:00
( $( $f :ident ) ::+ ( $ctx :expr , $( $arg :expr ) , * ) ) = > {
2024-06-15 12:45:23 +02:00
{
2024-06-15 14:55:23 +02:00
let ctx : & mut $crate ::context ::Context = $ctx ;
2024-07-02 16:44:04 -04:00
let ret = $( $f ) ::* ( ctx . ptr ( ) , $( $arg , ) * ) ;
2024-11-26 13:58:04 +01:00
if unsafe { raw ::err_code ( ctx . ptr ( ) ) = = raw ::err_NIX_ERR_KEY } {
2024-06-15 14:55:23 +02:00
ctx . clear ( ) ;
2024-06-15 12:45:23 +02:00
return Ok ( None ) ;
}
2024-06-15 14:55:23 +02:00
match ctx . check_err ( ) {
2024-06-15 12:45:23 +02:00
Ok ( _ ) = > Ok ( Some ( ret ) ) ,
Err ( e ) = > {
2024-06-15 14:55:23 +02:00
ctx . clear ( ) ;
2024-06-15 12:45:23 +02:00
Err ( e )
}
}
}
}
}
pub use check_call_opt_key ;
2024-02-16 09:59:51 +01:00
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn context_new_and_drop ( ) {
// don't crash
let _c = Context ::new ( ) ;
}
2024-06-15 14:34:30 +02:00
fn set_dummy_err ( ctx_ptr : * mut raw ::c_context ) {
unsafe {
raw ::set_err_msg (
ctx_ptr ,
2025-12-15 19:48:30 -05:00
raw ::err_NIX_ERR_UNKNOWN ,
c " dummy error message " . as_ptr ( ) ,
2024-06-15 14:34:30 +02:00
) ;
}
}
#[ test ]
fn check_call_dynamic_context ( ) {
2024-07-02 16:35:32 -04:00
let r = check_call! ( set_dummy_err ( & mut Context ::new ( ) ) ) ;
2024-06-15 14:34:30 +02:00
assert! ( r . is_err ( ) ) ;
assert_eq! ( r . unwrap_err ( ) . to_string ( ) , " dummy error message " ) ;
}
2024-02-16 09:59:51 +01:00
}