use core::cmp::Ordering;
use crate::{
Def, EnumRepr, EnumType, Facet, FieldBuilder, HashProxy, OptionDef, OptionVTable, OxPtrConst,
OxPtrMut, OxPtrUninit, OxRef, PtrConst, Repr, Shape, ShapeBuilder, Type, TypeOpsIndirect,
TypeParam, UserType, VTableIndirect, Variance, VarianceDep, VarianceDesc, VariantBuilder,
};
#[inline]
const fn get_option_def(shape: &'static Shape) -> Option<&'static OptionDef> {
match shape.def {
Def::Option(ref def) => Some(def),
_ => None,
}
}
fn option_type_name(
shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: crate::TypeNameOpts,
) -> core::fmt::Result {
write!(f, "Option")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
if let Some(tp) = shape.type_params.first() {
tp.shape.write_type_name(f, opts)?;
}
write!(f, ">")?;
} else {
write!(f, "<…>")?;
}
Ok(())
}
#[inline]
unsafe fn option_get_value_ptr(def: &OptionDef, ptr: PtrConst) -> Option<PtrConst> {
let raw = unsafe { (def.vtable.get_value)(ptr) };
if raw.is_null() {
None
} else {
Some(PtrConst::new_sized(raw))
}
}
unsafe fn option_display(
ox: OxPtrConst,
f: &mut core::fmt::Formatter<'_>,
) -> Option<core::fmt::Result> {
let shape = ox.shape();
let def = get_option_def(shape)?;
let ptr = ox.ptr();
if unsafe { (def.vtable.is_some)(ptr) } {
let inner_ptr = unsafe { option_get_value_ptr(def, ptr)? };
unsafe { def.t.call_display(inner_ptr, f) }
} else {
Some(f.write_str("None"))
}
}
unsafe fn option_debug(
ox: OxPtrConst,
f: &mut core::fmt::Formatter<'_>,
) -> Option<core::fmt::Result> {
let shape = ox.shape();
let def = get_option_def(shape)?;
let ptr = ox.ptr();
if unsafe { (def.vtable.is_some)(ptr) } {
let inner_ptr = unsafe { option_get_value_ptr(def, ptr)? };
let inner_ox = unsafe { OxRef::new(inner_ptr, def.t) };
Some(f.debug_tuple("Some").field(&inner_ox).finish())
} else {
Some(f.write_str("None"))
}
}
unsafe fn option_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
let shape = ox.shape();
let def = get_option_def(shape)?;
let ptr = ox.ptr();
use core::hash::Hash;
if unsafe { (def.vtable.is_some)(ptr) } {
1u8.hash(hasher);
let inner_ptr = unsafe { option_get_value_ptr(def, ptr)? };
unsafe { def.t.call_hash(inner_ptr, hasher)? };
} else {
0u8.hash(hasher);
}
Some(())
}
unsafe fn option_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
let shape = a.shape();
let def = get_option_def(shape)?;
let a_ptr = a.ptr();
let b_ptr = b.ptr();
let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
Some(match (a_is_some, b_is_some) {
(false, false) => true,
(true, true) => {
let a_inner = unsafe { option_get_value_ptr(def, a_ptr)? };
let b_inner = unsafe { option_get_value_ptr(def, b_ptr)? };
unsafe { def.t.call_partial_eq(a_inner, b_inner)? }
}
_ => false,
})
}
unsafe fn option_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
let shape = a.shape();
let def = get_option_def(shape)?;
let a_ptr = a.ptr();
let b_ptr = b.ptr();
let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
Some(match (a_is_some, b_is_some) {
(false, false) => Some(Ordering::Equal),
(false, true) => Some(Ordering::Less),
(true, false) => Some(Ordering::Greater),
(true, true) => {
let a_inner = unsafe { option_get_value_ptr(def, a_ptr)? };
let b_inner = unsafe { option_get_value_ptr(def, b_ptr)? };
unsafe { def.t.call_partial_cmp(a_inner, b_inner)? }
}
})
}
unsafe fn option_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
let shape = a.shape();
let def = get_option_def(shape)?;
let a_ptr = a.ptr();
let b_ptr = b.ptr();
let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
Some(match (a_is_some, b_is_some) {
(false, false) => Ordering::Equal,
(false, true) => Ordering::Less,
(true, false) => Ordering::Greater,
(true, true) => {
let a_inner = unsafe { option_get_value_ptr(def, a_ptr)? };
let b_inner = unsafe { option_get_value_ptr(def, b_ptr)? };
unsafe { def.t.call_cmp(a_inner, b_inner)? }
}
})
}
unsafe fn option_drop(ox: OxPtrMut) {
let shape = ox.shape();
let Some(def) = get_option_def(shape) else {
return;
};
let ptr = ox.ptr();
if unsafe { (def.vtable.is_some)(ptr.as_const()) } {
unsafe { option_drop_inner(ptr, def) };
}
}
unsafe fn option_drop_inner(ptr: crate::PtrMut, def: &OptionDef) {
unsafe { (def.vtable.replace_with)(ptr, core::ptr::null_mut()) };
}
unsafe fn option_default<T>(ox: OxPtrUninit) -> bool {
unsafe { ox.put(Option::<T>::None) };
true
}
unsafe fn option_is_some<T>(option: PtrConst) -> bool {
unsafe { option.get::<Option<T>>().is_some() }
}
unsafe extern "C" fn option_is_some_vtable<T>(option: PtrConst) -> bool {
unsafe { option_is_some::<T>(option) }
}
unsafe extern "C" fn option_get_value<T>(option: PtrConst) -> *const u8 {
unsafe {
option
.get::<Option<T>>()
.as_ref()
.map_or(core::ptr::null(), |t| t as *const T as *const u8)
}
}
unsafe extern "C" fn option_init_some<T>(
option: crate::PtrUninit,
value: crate::PtrMut,
) -> crate::PtrMut {
unsafe { option.put(Option::Some(value.read::<T>())) }
}
unsafe extern "C" fn option_init_none<T>(option: crate::PtrUninit) -> crate::PtrMut {
unsafe { option.put(<Option<T>>::None) }
}
unsafe extern "C" fn option_replace_with<T>(option: crate::PtrMut, value: *mut u8) {
unsafe {
let option = option.as_mut::<Option<T>>();
if value.is_null() {
option.take();
} else {
option.replace(crate::PtrMut::new_sized(value).read::<T>());
}
}
}
unsafe impl<'a, T: Facet<'a>> Facet<'a> for Option<T> {
const SHAPE: &'static Shape = &const {
const fn build_option_vtable<T>() -> OptionVTable {
OptionVTable::builder()
.is_some(option_is_some_vtable::<T>)
.get_value(option_get_value::<T>)
.init_some(option_init_some::<T>)
.init_none(option_init_none::<T>)
.replace_with(option_replace_with::<T>)
.build()
}
const fn build_vtable() -> VTableIndirect {
VTableIndirect {
display: Some(option_display),
debug: Some(option_debug),
hash: Some(option_hash),
invariants: None,
parse: None,
parse_bytes: None,
try_from: None,
try_into_inner: None,
try_borrow_inner: None,
partial_eq: Some(option_partial_eq),
partial_cmp: Some(option_partial_cmp),
cmp: Some(option_cmp),
}
}
const fn build_type_ops<T>() -> TypeOpsIndirect {
TypeOpsIndirect {
drop_in_place: option_drop,
default_in_place: Some(option_default::<T>),
clone_into: None,
is_truthy: Some(option_is_some::<T>),
}
}
ShapeBuilder::for_sized::<Option<T>>("Option")
.module_path("core::option")
.type_name(option_type_name)
.ty(Type::User(
if core::mem::size_of::<T>() == core::mem::size_of::<Option<T>>()
&& core::mem::size_of::<T>() <= core::mem::size_of::<usize>()
{
UserType::Enum(EnumType {
repr: Repr::default(),
enum_repr: EnumRepr::RustNPO,
variants: &const {
[
VariantBuilder::unit("None").discriminant(0).build(),
VariantBuilder::tuple(
"Some",
&const { [FieldBuilder::new("0", crate::shape_of::<T>, 0).build()] },
)
.discriminant(0)
.build(),
]
},
is_cow: false,
})
} else {
UserType::Enum(EnumType {
repr: Repr::default(),
enum_repr: EnumRepr::Rust,
variants: &const {
[
VariantBuilder::unit("None").discriminant(0).build(),
VariantBuilder::tuple(
"Some",
&const { [FieldBuilder::new("0", crate::shape_of::<T>, 0).build()] },
)
.discriminant(1)
.build(),
]
},
is_cow: false,
})
},
))
.def(Def::Option(OptionDef::new(
&const { build_option_vtable::<T>() },
T::SHAPE,
)))
.type_params(&[TypeParam {
name: "T",
shape: T::SHAPE,
}])
.inner(T::SHAPE)
.vtable_indirect(&const { build_vtable() })
.type_ops_indirect(&const { build_type_ops::<T>() })
.variance(VarianceDesc {
base: Variance::Bivariant,
deps: &const { [VarianceDep::covariant(T::SHAPE)] },
})
.build()
};
}