aboutsummaryrefslogtreecommitdiff
path: root/src/result.rs
blob: ba77858e3b23d6b42fd94bd23cfb1bce40b4bb21 (plain)
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
#![cfg(feature = "alloc")]
#![allow(missing_docs)]

use crate::exception::Exception;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use core::fmt::Display;
use core::ptr::{self, NonNull};
use core::result::Result as StdResult;
use core::slice;
use core::str;

#[repr(C)]
#[derive(Copy, Clone)]
pub struct PtrLen {
    pub ptr: NonNull<u8>,
    pub len: usize,
}

#[repr(C)]
pub union Result {
    err: PtrLen,
    ok: *const u8, // null
}

pub unsafe fn r#try<T, E>(ret: *mut T, result: StdResult<T, E>) -> Result
where
    E: Display,
{
    match result {
        Ok(ok) => {
            unsafe { ptr::write(ret, ok) }
            Result { ok: ptr::null() }
        }
        Err(err) => unsafe { to_c_error(err.to_string()) },
    }
}

unsafe fn to_c_error(msg: String) -> Result {
    let mut msg = msg;
    unsafe { msg.as_mut_vec() }.push(b'\0');
    let ptr = msg.as_ptr();
    let len = msg.len();

    extern "C" {
        #[link_name = "cxxbridge1$error"]
        fn error(ptr: *const u8, len: usize) -> NonNull<u8>;
    }

    let copy = unsafe { error(ptr, len) };
    let err = PtrLen { ptr: copy, len };
    Result { err }
}

impl Result {
    pub unsafe fn exception(self) -> StdResult<(), Exception> {
        unsafe {
            if self.ok.is_null() {
                Ok(())
            } else {
                let err = self.err;
                let slice = slice::from_raw_parts_mut(err.ptr.as_ptr(), err.len);
                let s = str::from_utf8_unchecked_mut(slice);
                Err(Exception {
                    what: Box::from_raw(s),
                })
            }
        }
    }
}