1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use ockam_core::{
    errcode::{Kind, Origin},
    Error,
};
use std::ffi::CString;
use std::os::raw::c_char;

#[repr(C)]
#[derive(Debug, PartialEq, Eq)]
/// Error type relating to FFI specific failures.
pub struct FfiOckamError {
    code: i32,
    domain: *const c_char,
}

impl FfiOckamError {
    /// Create a new error.
    pub fn new(code: i32, domain: &str) -> Self {
        Self {
            code,
            // TODO: Should this graciously fail?
            domain: CString::new(domain.as_bytes()).unwrap().into_raw(),
        }
    }

    /// No error.
    pub fn none() -> Self {
        Self {
            code: 0,
            domain: std::ptr::null(),
        }
    }
}

/// Represents the failures that can occur in an Ockam FFI Vault.
#[derive(Clone, Copy, Debug)]
pub enum FfiError {
    /// Persistence is not supported for this Vault implementation.
    VaultDoesNotSupportPersistence = 1,

    /// An underlying filesystem error prevented Vault creation.
    ErrorCreatingFilesystemVault,

    /// Invalid parameter.
    InvalidParam,

    /// Entry not found.
    EntryNotFound,

    /// Unknown public key type.
    UnknownPublicKeyType,

    /// Invalid string.
    InvalidString,

    /// Buffer is too small.
    BufferTooSmall,

    /// A public key is invalid.
    InvalidPublicKey,

    /// No such Vault.
    VaultNotFound,

    /// Ownership error.
    OwnershipError,

    /// Caught a panic (which would be UB if we let it unwind across the FFI).
    UnexpectedPanic,
}
impl ockam_core::compat::error::Error for FfiError {}
impl From<FfiError> for Error {
    #[track_caller]
    fn from(err: FfiError) -> Self {
        Error::new(Origin::Other, Kind::Other, err)
    }
}
impl core::fmt::Display for FfiError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::VaultDoesNotSupportPersistence => write!(
                f,
                "persistence is not supported for this Vault implementation."
            ),
            Self::ErrorCreatingFilesystemVault => write!(
                f,
                "an underlying filesystem error prevented Vault creation."
            ),
            Self::InvalidParam => write!(f, "invalid parameter."),
            Self::EntryNotFound => write!(f, "entry not found."),
            Self::UnknownPublicKeyType => write!(f, "unknown public key type."),
            Self::InvalidString => write!(f, "invalid string."),
            Self::BufferTooSmall => write!(f, "buffer is too small."),
            Self::InvalidPublicKey => write!(f, "a public key is invalid."),
            Self::VaultNotFound => write!(f, "no such Vault."),
            Self::OwnershipError => write!(f, "ownership error."),
            Self::UnexpectedPanic => write!(
                f,
                "caught a panic (which would be UB if we let it unwind across the FFI)."
            ),
        }
    }
}

impl From<Error> for FfiOckamError {
    fn from(err: Error) -> Self {
        Self::new(
            err.code().origin as i32 * 10_000 + err.code().kind as i32,
            "unknown",
        )
    }
}

impl From<FfiError> for FfiOckamError {
    fn from(err: FfiError) -> Self {
        let err2: Error = err.into();
        Self::from(err2)
    }
}

/// # Safety
/// frees `FfiOckamError::domain` if it's non-null
#[no_mangle]
pub unsafe extern "C" fn ockam_vault_free_error(context: &mut FfiOckamError) {
    if !context.domain.is_null() {
        let _ = CString::from_raw(context.domain as *mut _);
        context.domain = core::ptr::null();
    }
}