1use core::cmp::Ordering;
4
5use crate::{
6 Def, EnumRepr, EnumType, Facet, FieldBuilder, HashProxy, OptionDef, OptionVTable, OxPtrConst,
7 OxPtrMut, OxRef, PtrConst, Repr, Shape, ShapeBuilder, Type, TypeOpsIndirect, TypeParam,
8 UserType, VTableIndirect, VariantBuilder,
9};
10
11#[inline]
13fn get_option_def(shape: &'static Shape) -> Option<&'static OptionDef> {
14 match shape.def {
15 Def::Option(ref def) => Some(def),
16 _ => None,
17 }
18}
19
20unsafe fn option_display(
22 ox: OxPtrConst,
23 f: &mut core::fmt::Formatter<'_>,
24) -> Option<core::fmt::Result> {
25 let shape = ox.shape();
26 let def = get_option_def(shape)?;
27 let ptr = ox.ptr();
28
29 if unsafe { (def.vtable.is_some)(ptr) } {
30 let inner_ptr = unsafe { (def.vtable.get_value)(ptr)? };
32 unsafe { def.t.call_display(inner_ptr, f) }
34 } else {
35 Some(f.write_str("None"))
36 }
37}
38
39unsafe fn option_debug(
41 ox: OxPtrConst,
42 f: &mut core::fmt::Formatter<'_>,
43) -> Option<core::fmt::Result> {
44 let shape = ox.shape();
45 let def = get_option_def(shape)?;
46 let ptr = ox.ptr();
47
48 if unsafe { (def.vtable.is_some)(ptr) } {
49 let inner_ptr = unsafe { (def.vtable.get_value)(ptr)? };
51 let inner_ox = OxRef::new(inner_ptr, def.t);
52 Some(f.debug_tuple("Some").field(&inner_ox).finish())
53 } else {
54 Some(f.write_str("None"))
55 }
56}
57
58unsafe fn option_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
60 let shape = ox.shape();
61 let def = get_option_def(shape)?;
62 let ptr = ox.ptr();
63
64 use core::hash::Hash;
65 if unsafe { (def.vtable.is_some)(ptr) } {
66 1u8.hash(hasher);
67 let inner_ptr = unsafe { (def.vtable.get_value)(ptr)? };
68 unsafe { def.t.call_hash(inner_ptr, hasher)? };
69 } else {
70 0u8.hash(hasher);
71 }
72 Some(())
73}
74
75unsafe fn option_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
77 let shape = a.shape();
78 let def = get_option_def(shape)?;
79
80 let a_ptr = a.ptr();
81 let b_ptr = b.ptr();
82 let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
83 let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
84
85 Some(match (a_is_some, b_is_some) {
86 (false, false) => true,
87 (true, true) => {
88 let a_inner = unsafe { (def.vtable.get_value)(a_ptr)? };
89 let b_inner = unsafe { (def.vtable.get_value)(b_ptr)? };
90 unsafe { def.t.call_partial_eq(a_inner, b_inner)? }
91 }
92 _ => false,
93 })
94}
95
96unsafe fn option_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
98 let shape = a.shape();
99 let def = get_option_def(shape)?;
100
101 let a_ptr = a.ptr();
102 let b_ptr = b.ptr();
103 let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
104 let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
105
106 Some(match (a_is_some, b_is_some) {
107 (false, false) => Some(Ordering::Equal),
108 (false, true) => Some(Ordering::Less),
109 (true, false) => Some(Ordering::Greater),
110 (true, true) => {
111 let a_inner = unsafe { (def.vtable.get_value)(a_ptr)? };
112 let b_inner = unsafe { (def.vtable.get_value)(b_ptr)? };
113 unsafe { def.t.call_partial_cmp(a_inner, b_inner)? }
114 }
115 })
116}
117
118unsafe fn option_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
120 let shape = a.shape();
121 let def = get_option_def(shape)?;
122
123 let a_ptr = a.ptr();
124 let b_ptr = b.ptr();
125 let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
126 let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
127
128 Some(match (a_is_some, b_is_some) {
129 (false, false) => Ordering::Equal,
130 (false, true) => Ordering::Less,
131 (true, false) => Ordering::Greater,
132 (true, true) => {
133 let a_inner = unsafe { (def.vtable.get_value)(a_ptr)? };
134 let b_inner = unsafe { (def.vtable.get_value)(b_ptr)? };
135 unsafe { def.t.call_cmp(a_inner, b_inner)? }
136 }
137 })
138}
139
140unsafe fn option_drop(ox: OxPtrMut) {
142 let shape = ox.shape();
143 let Some(def) = get_option_def(shape) else {
144 return;
145 };
146 let ptr = ox.ptr();
147
148 if unsafe { (def.vtable.is_some)(ptr.as_const()) } {
149 unsafe { option_drop_inner(ptr, def) };
154 }
155}
156
157unsafe fn option_drop_inner(ptr: crate::PtrMut, def: &OptionDef) {
159 unsafe { (def.vtable.replace_with)(ptr, None) };
162}
163
164unsafe fn option_default<T>(ox: OxPtrMut) {
166 let ptr = ox.ptr();
167 unsafe { ptr.as_uninit().put(Option::<T>::None) };
168}
169
170unsafe fn option_is_some<T>(option: PtrConst) -> bool {
172 unsafe { option.get::<Option<T>>().is_some() }
173}
174
175unsafe fn option_get_value<T>(option: PtrConst) -> Option<PtrConst> {
177 unsafe {
178 option
179 .get::<Option<T>>()
180 .as_ref()
181 .map(|t| PtrConst::new(t as *const T))
182 }
183}
184
185unsafe fn option_init_some<T>(option: crate::PtrUninit, value: PtrConst) -> crate::PtrMut {
187 unsafe { option.put(Option::Some(value.read::<T>())) }
188}
189
190unsafe fn option_init_none<T>(option: crate::PtrUninit) -> crate::PtrMut {
192 unsafe { option.put(<Option<T>>::None) }
193}
194
195unsafe fn option_replace_with<T>(option: crate::PtrMut, value: Option<PtrConst>) {
197 unsafe {
198 let option = option.as_mut::<Option<T>>();
199 match value {
200 Some(value) => {
201 option.replace(value.read::<T>());
202 }
203 None => {
204 option.take();
205 }
206 };
207 }
208}
209
210unsafe impl<'a, T: Facet<'a>> Facet<'a> for Option<T> {
211 const SHAPE: &'static Shape = &const {
212 const fn build_option_vtable<T>() -> OptionVTable {
213 OptionVTable::builder()
214 .is_some(option_is_some::<T>)
215 .get_value(option_get_value::<T>)
216 .init_some(option_init_some::<T>)
217 .init_none(option_init_none::<T>)
218 .replace_with(option_replace_with::<T>)
219 .build()
220 }
221
222 const fn build_vtable() -> VTableIndirect {
223 VTableIndirect {
224 display: Some(option_display),
225 debug: Some(option_debug),
226 hash: Some(option_hash),
227 invariants: None,
228 parse: None,
229 parse_bytes: None,
230 try_from: None,
231 try_into_inner: None,
232 try_borrow_inner: None,
233 partial_eq: Some(option_partial_eq),
234 partial_cmp: Some(option_partial_cmp),
235 cmp: Some(option_cmp),
236 }
237 }
238
239 const fn build_type_ops<T>() -> TypeOpsIndirect {
240 TypeOpsIndirect {
241 drop_in_place: option_drop,
242 default_in_place: Some(option_default::<T>),
243 clone_into: None,
244 is_truthy: Some(option_is_some::<T>),
245 }
246 }
247
248 ShapeBuilder::for_sized::<Option<T>>("Option")
249 .ty(Type::User(
250 if core::mem::size_of::<T>() == core::mem::size_of::<Option<T>>()
252 && core::mem::size_of::<T>() <= core::mem::size_of::<usize>()
253 {
254 UserType::Enum(EnumType {
255 repr: Repr::default(),
256 enum_repr: EnumRepr::RustNPO,
257 variants: &const {
258 [
259 VariantBuilder::unit("None").discriminant(0).build(),
260 VariantBuilder::tuple(
261 "Some",
262 &const { [FieldBuilder::new("0", crate::shape_of::<T>, 0).build()] },
263 )
264 .discriminant(0)
265 .build(),
266 ]
267 },
268 })
269 } else {
270 UserType::Opaque
271 },
272 ))
273 .def(Def::Option(OptionDef::new(
274 &const { build_option_vtable::<T>() },
275 T::SHAPE,
276 )))
277 .type_params(&[TypeParam {
278 name: "T",
279 shape: T::SHAPE,
280 }])
281 .inner(T::SHAPE)
282 .vtable_indirect(&const { build_vtable() })
283 .type_ops_indirect(&const { build_type_ops::<T>() })
284 .variance(Shape::computed_variance)
286 .build()
287 };
288}