1use core::cmp::Ordering;
4
5use crate::{
6 Def, EnumRepr, EnumType, Facet, FieldBuilder, HashProxy, OptionDef, OptionVTable, OxPtrConst,
7 OxPtrMut, OxPtrUninit, OxRef, PtrConst, Repr, Shape, ShapeBuilder, Type, TypeOpsIndirect,
8 TypeParam, UserType, VTableIndirect, Variance, VarianceDep, VarianceDesc, VariantBuilder,
9};
10
11#[inline]
13const fn 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
20fn option_type_name(
21 shape: &'static Shape,
22 f: &mut core::fmt::Formatter<'_>,
23 opts: crate::TypeNameOpts,
24) -> core::fmt::Result {
25 write!(f, "Option")?;
26 if let Some(opts) = opts.for_children() {
27 write!(f, "<")?;
28 if let Some(tp) = shape.type_params.first() {
29 tp.shape.write_type_name(f, opts)?;
30 }
31 write!(f, ">")?;
32 } else {
33 write!(f, "<…>")?;
34 }
35 Ok(())
36}
37
38#[inline]
39unsafe fn option_get_value_ptr(def: &OptionDef, ptr: PtrConst) -> Option<PtrConst> {
40 let raw = unsafe { (def.vtable.get_value)(ptr) };
41 if raw.is_null() {
42 None
43 } else {
44 Some(PtrConst::new_sized(raw))
45 }
46}
47
48unsafe fn option_display(
50 ox: OxPtrConst,
51 f: &mut core::fmt::Formatter<'_>,
52) -> Option<core::fmt::Result> {
53 let shape = ox.shape();
54 let def = get_option_def(shape)?;
55 let ptr = ox.ptr();
56
57 if unsafe { (def.vtable.is_some)(ptr) } {
58 let inner_ptr = unsafe { option_get_value_ptr(def, ptr)? };
60 unsafe { def.t.call_display(inner_ptr, f) }
62 } else {
63 Some(f.write_str("None"))
64 }
65}
66
67unsafe fn option_debug(
69 ox: OxPtrConst,
70 f: &mut core::fmt::Formatter<'_>,
71) -> Option<core::fmt::Result> {
72 let shape = ox.shape();
73 let def = get_option_def(shape)?;
74 let ptr = ox.ptr();
75
76 if unsafe { (def.vtable.is_some)(ptr) } {
77 let inner_ptr = unsafe { option_get_value_ptr(def, ptr)? };
81 let inner_ox = unsafe { OxRef::new(inner_ptr, def.t) };
82 Some(f.debug_tuple("Some").field(&inner_ox).finish())
83 } else {
84 Some(f.write_str("None"))
85 }
86}
87
88unsafe fn option_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
90 let shape = ox.shape();
91 let def = get_option_def(shape)?;
92 let ptr = ox.ptr();
93
94 use core::hash::Hash;
95 if unsafe { (def.vtable.is_some)(ptr) } {
96 1u8.hash(hasher);
97 let inner_ptr = unsafe { option_get_value_ptr(def, ptr)? };
98 unsafe { def.t.call_hash(inner_ptr, hasher)? };
99 } else {
100 0u8.hash(hasher);
101 }
102 Some(())
103}
104
105unsafe fn option_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
107 let shape = a.shape();
108 let def = get_option_def(shape)?;
109
110 let a_ptr = a.ptr();
111 let b_ptr = b.ptr();
112 let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
113 let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
114
115 Some(match (a_is_some, b_is_some) {
116 (false, false) => true,
117 (true, true) => {
118 let a_inner = unsafe { option_get_value_ptr(def, a_ptr)? };
119 let b_inner = unsafe { option_get_value_ptr(def, b_ptr)? };
120 unsafe { def.t.call_partial_eq(a_inner, b_inner)? }
121 }
122 _ => false,
123 })
124}
125
126unsafe fn option_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
128 let shape = a.shape();
129 let def = get_option_def(shape)?;
130
131 let a_ptr = a.ptr();
132 let b_ptr = b.ptr();
133 let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
134 let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
135
136 Some(match (a_is_some, b_is_some) {
137 (false, false) => Some(Ordering::Equal),
138 (false, true) => Some(Ordering::Less),
139 (true, false) => Some(Ordering::Greater),
140 (true, true) => {
141 let a_inner = unsafe { option_get_value_ptr(def, a_ptr)? };
142 let b_inner = unsafe { option_get_value_ptr(def, b_ptr)? };
143 unsafe { def.t.call_partial_cmp(a_inner, b_inner)? }
144 }
145 })
146}
147
148unsafe fn option_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
150 let shape = a.shape();
151 let def = get_option_def(shape)?;
152
153 let a_ptr = a.ptr();
154 let b_ptr = b.ptr();
155 let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
156 let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
157
158 Some(match (a_is_some, b_is_some) {
159 (false, false) => Ordering::Equal,
160 (false, true) => Ordering::Less,
161 (true, false) => Ordering::Greater,
162 (true, true) => {
163 let a_inner = unsafe { option_get_value_ptr(def, a_ptr)? };
164 let b_inner = unsafe { option_get_value_ptr(def, b_ptr)? };
165 unsafe { def.t.call_cmp(a_inner, b_inner)? }
166 }
167 })
168}
169
170unsafe fn option_drop(ox: OxPtrMut) {
172 let shape = ox.shape();
173 let Some(def) = get_option_def(shape) else {
174 return;
175 };
176 let ptr = ox.ptr();
177
178 if unsafe { (def.vtable.is_some)(ptr.as_const()) } {
179 unsafe { option_drop_inner(ptr, def) };
184 }
185}
186
187unsafe fn option_drop_inner(ptr: crate::PtrMut, def: &OptionDef) {
189 unsafe { (def.vtable.replace_with)(ptr, core::ptr::null_mut()) };
192}
193
194unsafe fn option_default<T>(ox: OxPtrUninit) -> bool {
196 unsafe { ox.put(Option::<T>::None) };
197 true
198}
199
200unsafe fn option_is_some<T>(option: PtrConst) -> bool {
202 unsafe { option.get::<Option<T>>().is_some() }
203}
204
205unsafe extern "C" fn option_is_some_vtable<T>(option: PtrConst) -> bool {
207 unsafe { option_is_some::<T>(option) }
208}
209
210unsafe extern "C" fn option_get_value<T>(option: PtrConst) -> *const u8 {
212 unsafe {
213 option
214 .get::<Option<T>>()
215 .as_ref()
216 .map_or(core::ptr::null(), |t| t as *const T as *const u8)
217 }
218}
219
220unsafe extern "C" fn option_init_some<T>(
222 option: crate::PtrUninit,
223 value: crate::PtrMut,
224) -> crate::PtrMut {
225 unsafe { option.put(Option::Some(value.read::<T>())) }
226}
227
228unsafe extern "C" fn option_init_none<T>(option: crate::PtrUninit) -> crate::PtrMut {
230 unsafe { option.put(<Option<T>>::None) }
231}
232
233unsafe extern "C" fn option_replace_with<T>(option: crate::PtrMut, value: *mut u8) {
235 unsafe {
236 let option = option.as_mut::<Option<T>>();
237 if value.is_null() {
238 option.take();
239 } else {
240 option.replace(crate::PtrMut::new_sized(value).read::<T>());
241 }
242 }
243}
244
245unsafe impl<'a, T: Facet<'a>> Facet<'a> for Option<T> {
246 const SHAPE: &'static Shape = &const {
247 const fn build_option_vtable<T>() -> OptionVTable {
248 OptionVTable::builder()
249 .is_some(option_is_some_vtable::<T>)
250 .get_value(option_get_value::<T>)
251 .init_some(option_init_some::<T>)
252 .init_none(option_init_none::<T>)
253 .replace_with(option_replace_with::<T>)
254 .build()
255 }
256
257 const fn build_vtable() -> VTableIndirect {
258 VTableIndirect {
259 display: Some(option_display),
260 debug: Some(option_debug),
261 hash: Some(option_hash),
262 invariants: None,
263 parse: None,
264 parse_bytes: None,
265 try_from: None,
266 try_into_inner: None,
267 try_borrow_inner: None,
268 partial_eq: Some(option_partial_eq),
269 partial_cmp: Some(option_partial_cmp),
270 cmp: Some(option_cmp),
271 }
272 }
273
274 const fn build_type_ops<T>() -> TypeOpsIndirect {
275 TypeOpsIndirect {
276 drop_in_place: option_drop,
277 default_in_place: Some(option_default::<T>),
278 clone_into: None,
279 is_truthy: Some(option_is_some::<T>),
280 }
281 }
282
283 ShapeBuilder::for_sized::<Option<T>>("Option")
284 .module_path("core::option")
285 .type_name(option_type_name)
286 .ty(Type::User(
287 if core::mem::size_of::<T>() == core::mem::size_of::<Option<T>>()
289 && core::mem::size_of::<T>() <= core::mem::size_of::<usize>()
290 {
291 UserType::Enum(EnumType {
292 repr: Repr::default(),
293 enum_repr: EnumRepr::RustNPO,
294 variants: &const {
295 [
296 VariantBuilder::unit("None").discriminant(0).build(),
297 VariantBuilder::tuple(
298 "Some",
299 &const { [FieldBuilder::new("0", crate::shape_of::<T>, 0).build()] },
300 )
301 .discriminant(0)
302 .build(),
303 ]
304 },
305 is_cow: false,
306 })
307 } else {
308 UserType::Enum(EnumType {
309 repr: Repr::default(),
310 enum_repr: EnumRepr::Rust,
311 variants: &const {
312 [
313 VariantBuilder::unit("None").discriminant(0).build(),
314 VariantBuilder::tuple(
315 "Some",
316 &const { [FieldBuilder::new("0", crate::shape_of::<T>, 0).build()] },
317 )
318 .discriminant(1)
319 .build(),
320 ]
321 },
322 is_cow: false,
323 })
324 },
325 ))
326 .def(Def::Option(OptionDef::new(
327 &const { build_option_vtable::<T>() },
328 T::SHAPE,
329 )))
330 .type_params(&[TypeParam {
331 name: "T",
332 shape: T::SHAPE,
333 }])
334 .inner(T::SHAPE)
335 .vtable_indirect(&const { build_vtable() })
336 .type_ops_indirect(&const { build_type_ops::<T>() })
337 .variance(VarianceDesc {
339 base: Variance::Bivariant,
340 deps: &const { [VarianceDep::covariant(T::SHAPE)] },
341 })
342 .build()
343 };
344}