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