use crate::lang::c::{CType, CompositeType, Documentation, Field, Meta, PrimitiveType, Visibility};
use crate::lang::rust::CTypeInfo;
use crate::patterns::primitives::FFIBool;
use crate::patterns::TypePattern;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Debug, Copy, Clone, PartialEq, Default, Deserialize, Serialize))]
#[cfg_attr(not(feature = "serde"), derive(Debug, Copy, Clone, PartialEq, Default))]
pub struct FFIOption<T> {
t: T,
is_some: FFIBool,
}
impl<T> FFIOption<T> {
pub const fn some(data: T) -> Self {
Self { is_some: FFIBool::TRUE, t: data }
}
#[allow(clippy::missing_const_for_fn)]
pub fn into_option(self) -> Option<T> {
match self.is_some.is() {
true => Option::Some(self.t),
false => Option::None,
}
}
pub fn as_ref(&self) -> Option<&T> {
match self.is_some.is() {
true => Option::Some(&self.t),
false => Option::None,
}
}
pub fn as_mut(&mut self) -> Option<&mut T> {
match self.is_some.is() {
true => Option::Some(&mut self.t),
false => Option::None,
}
}
pub fn is_some(&self) -> bool {
self.is_some.is()
}
pub fn is_none(&self) -> bool {
!self.is_some()
}
#[track_caller]
pub fn unwrap(self) -> T {
if self.is_some.is() {
self.t
} else {
panic!("Trying to unwrap None value");
}
}
#[track_caller]
pub fn unwrap_as_mut(&mut self) -> &mut T {
if self.is_some.is() {
&mut self.t
} else {
panic!("Trying to unwrap None value");
}
}
}
impl<T: Default> FFIOption<T> {
pub fn none() -> Self {
Self {
is_some: FFIBool::FALSE,
t: T::default(),
}
}
}
impl<T: Default> From<Option<T>> for FFIOption<T> {
fn from(option: Option<T>) -> Self {
match option {
Option::None => Self::none(),
Option::Some(t) => Self::some(t),
}
}
}
unsafe impl<T> CTypeInfo for FFIOption<T>
where
T: CTypeInfo,
{
fn type_info() -> CType {
let doc_t = Documentation::from_line("Element that is maybe valid.");
let doc_is_some = Documentation::from_line("Byte where `1` means element `t` is valid.");
let fields = vec![
Field::with_documentation("t".to_string(), T::type_info(), Visibility::Private, doc_t),
Field::with_documentation("is_some".to_string(), CType::Primitive(PrimitiveType::U8), Visibility::Private, doc_is_some),
];
let doc = Documentation::from_line("Option type containing boolean flag and maybe valid data.");
let meta = Meta::with_namespace_documentation(T::type_info().namespace().map(|e| e.into()).unwrap_or_else(String::new), doc);
let composite = CompositeType::with_meta(format!("Option{}", T::type_info().name_within_lib()), fields, meta);
CType::Pattern(TypePattern::Option(composite))
}
}
#[cfg(test)]
mod test {
use crate::patterns::option::FFIOption;
#[test]
fn can_create() {
assert!(FFIOption::some(100).is_some());
assert!(FFIOption::<u8>::none().is_none());
}
}