use core::fmt; use core::mem::ManuallyDrop; #[repr(C)] union DiplomatResultValue { ok: ManuallyDrop, err: ManuallyDrop, } /// A [`Result`]-like type that can be passed across the FFI boundary /// as a value. Used internally to return [`Result`]s and [`Option`]s /// from functions. #[repr(C)] pub struct DiplomatResult { value: DiplomatResultValue, pub is_ok: bool, } /// A type to represent Option over FFI. /// /// Used internally to handle `Option` arguments and return types, and needs to be /// used explicitly for optional struct fields. pub type DiplomatOption = DiplomatResult; impl DiplomatResult { pub fn as_ref(&self) -> Result<&T, &E> { // Safety: we're only accessing the union variants when the flag is correct unsafe { if self.is_ok { Ok(&self.value.ok) } else { Err(&self.value.err) } } } } impl DiplomatOption { /// Helper for converting into an Option to avoid trait ambiguity errors with Into #[inline] pub fn into_option(self) -> Option { self.into() } /// Helper for converting into an Option with the inner type converted #[inline] pub fn into_converted_option>(self) -> Option { self.into_option().map(Into::into) } } impl Clone for DiplomatResult { fn clone(&self) -> Self { unsafe { if self.is_ok { Ok((*self.value.ok).clone()).into() } else { Err((*self.value.err).clone()).into() } } } } impl Drop for DiplomatResult { fn drop(&mut self) { unsafe { if self.is_ok { let _ = ManuallyDrop::take(&mut self.value.ok); } else { let _ = ManuallyDrop::take(&mut self.value.err); } } } } impl From> for DiplomatResult { fn from(result: Result) -> Self { match result { Result::Ok(ok) => DiplomatResult { value: DiplomatResultValue { ok: ManuallyDrop::new(ok), }, is_ok: true, }, Result::Err(err) => DiplomatResult { value: DiplomatResultValue { err: ManuallyDrop::new(err), }, is_ok: false, }, } } } impl From> for DiplomatOption { fn from(option: Option) -> Self { option.ok_or(()).into() } } impl From> for Option { fn from(result: DiplomatOption) -> Self { Result::::from(result).ok() } } impl From> for Result { fn from(mut result: DiplomatResult) -> Result { unsafe { if result.is_ok { Ok(ManuallyDrop::take(&mut result.value.ok)) } else { Err(ManuallyDrop::take(&mut result.value.err)) } } } } impl fmt::Debug for DiplomatResult { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.as_ref().fmt(f) } }