pkgcraft/
error.rs

1use std::cell::RefCell;
2use std::ffi::{c_char, CString};
3use std::{fmt, ptr};
4
5use crate::macros::*;
6
7#[derive(Debug, Clone)]
8#[repr(C)]
9pub enum ErrorKind {
10    Generic,
11    Pkgcraft,
12    Config,
13    Repo,
14    Pkg,
15}
16
17#[derive(Debug, Clone)]
18pub struct Error {
19    message: String,
20    kind: ErrorKind,
21}
22
23impl Error {
24    pub fn new<S: ToString>(s: S) -> Self {
25        Error {
26            message: s.to_string(),
27            kind: ErrorKind::Generic,
28        }
29    }
30}
31
32impl fmt::Display for Error {
33    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34        write!(f, "{}", self.message)
35    }
36}
37
38impl std::error::Error for Error {}
39
40impl From<pkgcraft::Error> for Error {
41    fn from(e: pkgcraft::Error) -> Self {
42        use pkgcraft::Error::*;
43        let kind = match &e {
44            Config(_) => ErrorKind::Config,
45            InvalidPkg { .. } => ErrorKind::Pkg,
46            InvalidRepo { .. } => ErrorKind::Repo,
47            RepoInit(_) => ErrorKind::Repo,
48            _ => ErrorKind::Pkgcraft,
49        };
50
51        Error { message: e.to_string(), kind }
52    }
53}
54
55impl From<std::str::Utf8Error> for Error {
56    fn from(e: std::str::Utf8Error) -> Self {
57        Error::new(e)
58    }
59}
60
61impl From<std::ffi::NulError> for Error {
62    fn from(e: std::ffi::NulError) -> Self {
63        Error::new(e)
64    }
65}
66
67impl From<&str> for Error {
68    fn from(e: &str) -> Self {
69        Error::new(e)
70    }
71}
72
73#[repr(C)]
74pub struct PkgcraftError {
75    message: *mut c_char,
76    kind: ErrorKind,
77}
78
79impl From<Error> for PkgcraftError {
80    fn from(e: Error) -> Self {
81        PkgcraftError {
82            message: try_ptr_from_str!(e.message),
83            kind: e.kind,
84        }
85    }
86}
87
88impl Drop for PkgcraftError {
89    fn drop(&mut self) {
90        unsafe {
91            drop(CString::from_raw(self.message));
92        }
93    }
94}
95
96thread_local! {
97    static LAST_ERROR: RefCell<Option<Error>> = const { RefCell::new(None) };
98}
99
100/// Update the most recent error, clearing the previous value.
101pub(crate) fn update_last_error<E: Into<Error> + fmt::Debug>(err: E) {
102    LAST_ERROR.with(|prev| *prev.borrow_mut() = Some(err.into()));
103}
104
105/// Get the most recent error, returns NULL if none exists.
106#[no_mangle]
107pub extern "C" fn pkgcraft_error_last() -> *mut PkgcraftError {
108    match LAST_ERROR.with(|prev| prev.borrow_mut().take()) {
109        Some(e) => Box::into_raw(Box::new(e.into())),
110        None => ptr::null_mut(),
111    }
112}
113
114/// Free an error.
115///
116/// # Safety
117/// The argument must be a non-null PkgcraftError pointer or NULL.
118#[no_mangle]
119pub unsafe extern "C" fn pkgcraft_error_free(e: *mut PkgcraftError) {
120    if !e.is_null() {
121        unsafe { drop(Box::from_raw(e)) };
122    }
123}