rkyv/niche/
niched_option.rs1use core::{cmp, fmt, marker::PhantomData, mem::MaybeUninit, ops::Deref};
4
5use munge::munge;
6use rancor::Fallible;
7
8use super::niching::Niching;
9use crate::{seal::Seal, Archive, Place, Portable, Serialize};
10
11#[derive(Portable)]
16#[rkyv(crate)]
17#[repr(transparent)]
18pub struct NichedOption<T, N: ?Sized> {
19 repr: MaybeUninit<T>,
20 _niching: PhantomData<N>,
21}
22
23#[cfg(feature = "bytecheck")]
24const _: () = {
25 use core::ptr::addr_of;
26
27 use crate::bytecheck::CheckBytes;
28
29 unsafe impl<T, N, C> CheckBytes<C> for NichedOption<T, N>
30 where
31 T: CheckBytes<C>,
32 N: Niching<T> + ?Sized,
33 C: Fallible + ?Sized,
34 {
35 unsafe fn check_bytes(
36 value: *const Self,
37 context: &mut C,
38 ) -> Result<(), C::Error> {
39 let ptr = unsafe { addr_of!((*value).repr).cast::<T>() };
40 let is_niched = unsafe { N::is_niched(ptr) };
41
42 if !is_niched {
43 unsafe {
44 T::check_bytes(ptr, context)?;
45 }
46 }
47 Ok(())
48 }
49 }
50};
51
52impl<T, N: Niching<T> + ?Sized> NichedOption<T, N> {
53 pub fn is_none(&self) -> bool {
55 unsafe { N::is_niched(self.repr.as_ptr()) }
56 }
57
58 pub fn is_some(&self) -> bool {
60 !self.is_none()
61 }
62
63 pub fn as_ref(&self) -> Option<&T> {
65 if self.is_none() {
66 None
67 } else {
68 Some(unsafe { self.repr.assume_init_ref() })
69 }
70 }
71
72 pub fn as_mut(&mut self) -> Option<&mut T> {
74 if self.is_none() {
75 None
76 } else {
77 Some(unsafe { self.repr.assume_init_mut() })
78 }
79 }
80
81 pub fn as_seal(this: Seal<'_, Self>) -> Option<Seal<'_, T>> {
83 let this = unsafe { Seal::unseal_unchecked(this) };
84 this.as_mut().map(Seal::new)
85 }
86
87 pub fn iter(&self) -> Iter<&'_ T> {
89 Iter::new(self.as_ref())
90 }
91
92 pub fn iter_mut(&mut self) -> Iter<&'_ mut T> {
94 Iter::new(self.as_mut())
95 }
96
97 pub fn iter_seal(this: Seal<'_, Self>) -> Iter<Seal<'_, T>> {
99 Iter::new(Self::as_seal(this))
100 }
101
102 pub fn resolve_from_option<U>(
104 option: Option<&U>,
105 resolver: Option<U::Resolver>,
106 out: Place<Self>,
107 ) where
108 U: Archive<Archived = T>,
109 {
110 let out = Self::munge_place(out);
111 match option {
112 Some(value) => {
113 let resolver = resolver.expect("non-niched resolver");
114 value.resolve(resolver, out);
115 }
116 None => N::resolve_niched(out),
117 }
118 }
119
120 pub fn serialize_from_option<U, S>(
122 option: Option<&U>,
123 serializer: &mut S,
124 ) -> Result<Option<U::Resolver>, S::Error>
125 where
126 U: Serialize<S, Archived = T>,
127 S: Fallible + ?Sized,
128 {
129 match option {
130 Some(value) => value.serialize(serializer).map(Some),
131 None => Ok(None),
132 }
133 }
134
135 pub(crate) fn munge_place(out: Place<Self>) -> Place<T> {
136 munge!(let Self { repr, .. } = out);
137
138 unsafe { repr.cast_unchecked::<T>() }
139 }
140}
141
142impl<T, N> NichedOption<T, N>
143where
144 T: Deref,
145 N: Niching<T> + ?Sized,
146{
147 pub fn as_deref(&self) -> Option<&<T as Deref>::Target> {
149 self.as_ref().map(Deref::deref)
150 }
151}
152
153impl<T, N> fmt::Debug for NichedOption<T, N>
154where
155 T: fmt::Debug,
156 N: Niching<T> + ?Sized,
157{
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 self.as_ref().fmt(f)
160 }
161}
162
163impl<T, N> Eq for NichedOption<T, N>
164where
165 T: Eq,
166 N: Niching<T> + ?Sized,
167{
168}
169
170impl<T, N> PartialEq for NichedOption<T, N>
171where
172 T: PartialEq,
173 N: Niching<T> + ?Sized,
174{
175 fn eq(&self, other: &Self) -> bool {
176 self.as_ref().eq(&other.as_ref())
177 }
178}
179
180impl<T, N, Rhs> PartialEq<Option<Rhs>> for NichedOption<T, N>
181where
182 T: PartialEq<Rhs>,
183 N: Niching<T> + ?Sized,
184{
185 fn eq(&self, other: &Option<Rhs>) -> bool {
186 match (self.as_ref(), other) {
187 (Some(self_value), Some(other_value)) => self_value.eq(other_value),
188 (None, None) => true,
189 _ => false,
190 }
191 }
192}
193
194impl<T, N> Ord for NichedOption<T, N>
195where
196 T: Ord,
197 N: Niching<T> + ?Sized,
198{
199 fn cmp(&self, other: &Self) -> cmp::Ordering {
200 self.as_ref().cmp(&other.as_ref())
201 }
202}
203
204impl<T, N> PartialOrd for NichedOption<T, N>
205where
206 T: PartialOrd,
207 N: Niching<T> + ?Sized,
208{
209 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
210 self.as_ref().partial_cmp(&other.as_ref())
211 }
212}
213
214pub type Iter<P> = crate::option::Iter<P>;