Skip to main content

onepassword_sys/
errors.rs

1use byteorder::{BigEndian, ByteOrder};
2
3use crate::buffer::RustBuffer;
4
5pub type FfiResult<T = (), E = Error> = Result<T, E>;
6
7#[derive(Debug)]
8pub enum Error {
9    Error { code: i32, message: String },
10}
11
12impl Error {
13    pub fn code(&self) -> i32 {
14        match self {
15            Self::Error { code, .. } => *code,
16        }
17    }
18}
19
20#[repr(u8)]
21#[expect(dead_code)]
22pub(crate) enum CallStatusCode {
23    Success = 0,
24    Error = 1,
25    Panic = 2,
26}
27
28#[repr(C)]
29pub(crate) struct CallStatus {
30    pub code: CallStatusCode,
31    pub error_buf: RustBuffer,
32}
33
34pub(crate) trait ErrorConverter {
35    type ErrorType;
36
37    fn lift(buf: RustBuffer) -> Self::ErrorType;
38}
39
40pub(crate) struct NoConverter;
41impl ErrorConverter for NoConverter {
42    type ErrorType = std::convert::Infallible;
43
44    fn lift(_buf: RustBuffer) -> Self::ErrorType {
45        panic!("_rust_call_with_error: CALL_ERROR, but error_ffi_converter is None");
46    }
47}
48
49pub(crate) struct StringConverter;
50impl ErrorConverter for StringConverter {
51    type ErrorType = String;
52
53    fn lift(buf: RustBuffer) -> Self::ErrorType {
54        buf.to_string()
55    }
56}
57
58pub(crate) struct ErrorTypeConverter;
59impl ErrorConverter for ErrorTypeConverter {
60    type ErrorType = Error;
61
62    fn lift(buf: RustBuffer) -> Self::ErrorType {
63        let error_variant = BigEndian::read_i32(buf.as_ref());
64        let error_code = BigEndian::read_i32(&buf.as_ref()[4..]);
65
66        match error_variant {
67            1 => {
68                let remainder = &buf.as_ref()[8..];
69                let msg = String::from_utf8_lossy(remainder);
70                Error::Error {
71                    code: error_code,
72                    message: msg.into_owned(),
73                }
74            }
75            other => unimplemented!("unknown error variant {other}"),
76        }
77    }
78}
79
80pub(crate) fn check_call_status<C: ErrorConverter>(
81    call_status: CallStatus,
82) -> Result<(), C::ErrorType> {
83    match call_status.code {
84        CallStatusCode::Success => Ok(()),
85        CallStatusCode::Error => Err(C::lift(call_status.error_buf)),
86        CallStatusCode::Panic => {
87            let msg = if call_status.error_buf.len > 0 {
88                StringConverter::lift(call_status.error_buf)
89            } else {
90                "Unknown rust panic".to_string()
91            };
92            panic!("{msg}");
93        }
94    }
95}