tesseract_swift_utils/
error.rs

1use crate::panic::FromPanic;
2use crate::result::{Zip1, Result};
3
4use super::string::*;
5use super::traits::TryAsRef;
6use std::error::Error;
7use std::fmt::Display;
8use std::mem::ManuallyDrop;
9
10/// cbindgen:add-sentinel
11#[repr(u32)]
12#[derive(Debug, Copy, Clone, Eq, Ord, PartialOrd, PartialEq)]
13pub enum CErrorCode {
14    Null = 0, Panic, Utf8, Cast, Swift
15}
16
17pub trait ErrorCode: Copy {
18    fn from_u32(val: u32) -> Option<Self> {
19        if val < Self::FIRST || val >= Self::Sentinel { return  None; }
20        unsafe { Some(std::mem::transmute_copy(&val)) }
21    }
22    const FIRST: u32;
23    #[allow(non_upper_case_globals)]
24    const Sentinel: u32;
25}
26
27impl ErrorCode for CErrorCode {
28    const FIRST: u32 = Self::Null as u32;
29    #[allow(non_upper_case_globals)]
30    const Sentinel: u32 = Self::Swift as u32 + 1;
31}
32
33#[repr(C)]
34#[derive(Debug, Clone)]
35pub struct CError {
36    pub code: u32,
37    pub reason: CString,
38}
39
40impl CError {
41    pub fn new(code: u32, reason: String) -> Self {
42        Self { code: code, reason: reason.into() }
43    }
44
45    pub fn null<T: ?Sized>() -> Self {
46        Self::new(CErrorCode::Null as u32, std::any::type_name::<T>().into())
47    }
48
49    pub fn panic(reason: String) -> Self {
50        Self::new(CErrorCode::Panic as u32, reason.into())
51    }
52
53    pub fn utf8(reason: String) -> Self {
54        Self::new(CErrorCode::Utf8 as u32, reason.into())
55    }
56
57    pub fn swift(reason: String) -> Self {
58        Self::new(CErrorCode::Swift as u32, reason.into())
59    }
60
61    pub fn cast<F: ?Sized, T: ?Sized>() -> Self {
62        Self::new(
63            CErrorCode::Cast as u32, 
64            format!("Can't cast {} into {}",
65                std::any::type_name::<F>(),
66                std::any::type_name::<T>()
67            )
68        )
69    }
70}
71
72impl From<std::str::Utf8Error> for CError {
73    fn from(error: std::str::Utf8Error) -> Self {
74        Self::utf8(error.to_string())
75    }
76}
77
78impl From<std::ffi::IntoStringError> for CError {
79    fn from(error: std::ffi::IntoStringError) -> Self {
80        Self::utf8(error.to_string())
81    }
82}
83
84impl Display for CError {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        match CErrorCode::from_u32(self.code) {
87            None => write!(f, "Error[{}]", self.code),
88            Some(code) => match code {
89                CErrorCode::Null => write!(f, "Error::Null"),
90                CErrorCode::Panic => write!(f, "Error::Panic"),
91                CErrorCode::Utf8 => write!(f, "Error::UTF8"),
92                CErrorCode::Cast => write!(f, "Error::Cast"),
93                CErrorCode::Swift => write!(f, "Error::Swift")
94            }
95        }?;
96        let reason = self.reason.try_as_ref().map_err(|_| std::fmt::Error)?;
97        if reason.len() > 0 { write!(f, ": {}", reason)? }
98        Ok(())
99    }
100}
101
102impl Error for CError {}
103
104impl FromPanic for CError {
105    fn from_panic(panic: &str) -> Self {
106        Self::panic(panic.to_owned().into())
107    }
108}
109
110#[repr(C)]
111#[derive(Debug, Clone)]
112pub struct SwiftError {
113    pub code: isize,
114    pub domain: CString,
115    pub description: CString
116}
117
118impl SwiftError {
119    pub fn new(code: isize, domain: CStringRef, description: CStringRef) -> Self {
120        Self { code, domain: domain.try_as_ref().unwrap().into(),
121               description: description.try_as_ref().unwrap().into() }
122    }
123}
124
125impl TryFrom<SwiftError> for CError {
126    type Error = CError;
127
128    fn try_from(value: SwiftError) -> Result<Self> {
129        value.domain.try_as_ref().zip(value.description.try_as_ref())
130            .map(|(dm, ds)| {
131                format!("{} ~~~ {} ~~~ {}", dm, value.code, ds)
132            }).map(|str| CError::new(CErrorCode::Swift as u32, str))
133    }
134}
135
136impl TryFrom<&CError> for SwiftError {
137    type Error = CError;
138
139    fn try_from(value: &CError) -> Result<Self> {
140        if value.code != CErrorCode::Swift as u32 { return Err(CError::cast::<CError, SwiftError>()); }
141        let reason = value.reason.try_as_ref()?;
142        let parts: Vec<&str> = reason.split(" ~~~ ").collect();
143        if parts.len() != 3 { return Err(CError::cast::<CError, SwiftError>()); }
144        let code = parts[1].parse::<isize>().map_err(|_| CError::cast::<CError, SwiftError>())?;
145        Ok(Self { code, domain: parts[0].to_owned().into(), description: parts[2].to_owned().into() })
146    }
147}
148
149impl Display for SwiftError {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        write!(f, "{} ~~~ {} ~~~ {}", self.domain, self.code, self.description)
152    }
153}
154
155impl Error for SwiftError {}
156
157#[no_mangle]
158pub unsafe extern "C" fn tesseract_utils_swift_error_new(
159    code: isize, domain: CStringRef, description: CStringRef
160) -> ManuallyDrop<SwiftError> {
161    ManuallyDrop::new(SwiftError::new(code, domain, description))
162}
163
164#[no_mangle]
165pub unsafe extern "C" fn tesseract_utils_cerr_new_swift_error(
166    code: isize, domain: CStringRef, description: CStringRef
167) -> ManuallyDrop<CError> {
168    ManuallyDrop::new(SwiftError::new(code, domain, description).try_into().unwrap())
169}
170
171#[no_mangle]
172pub unsafe extern "C" fn tesseract_utils_cerr_get_description(
173    err: &CError
174) -> ManuallyDrop<CString> {
175    ManuallyDrop::new(err.to_string().into())
176}
177
178#[no_mangle]
179pub unsafe extern "C" fn tesseract_utils_cerr_get_swift_error(error: &CError) -> ManuallyDrop<SwiftError> {
180    ManuallyDrop::new(SwiftError::try_from(error).unwrap())
181}
182
183#[no_mangle]
184pub unsafe extern "C" fn tesseract_utils_swift_error_free(err: &mut ManuallyDrop<SwiftError>) {
185    ManuallyDrop::drop(err);
186}
187
188#[no_mangle]
189pub unsafe extern "C" fn tesseract_utils_cerror_free(err: &mut ManuallyDrop<CError>) {
190    ManuallyDrop::drop(err);
191}