abi_stable/nonexhaustive_enum/
nonexhaustive.rs

1//! Contains `NonExhaustive<>` and related items.
2
3use std::{
4    cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
5    fmt::{self, Debug, Display},
6    hash::{Hash, Hasher},
7    marker::PhantomData,
8    mem::ManuallyDrop,
9    ops::Deref,
10};
11
12use crate::{
13    abi_stability::StableAbi,
14    erased_types::{c_functions, trait_objects::HasherObject, InterfaceType, MakeRequiredTraits},
15    inline_storage::ScratchSpace,
16    marker_type::ErasedObject,
17    nonexhaustive_enum::{
18        assert_correct_storage, vtable::NonExhaustiveVtable_Ref, AssertCsArgs, DeserializeEnum,
19        EnumInfo, GetEnumInfo, GetVTable, NonExhaustiveMarker, SerializeEnum, ValidDiscriminant,
20    },
21    pointer_trait::{CanTransmuteElement, TransmuteElement},
22    sabi_types::{RMut, RRef},
23    std_types::RBoxError,
24    traits::IntoReprRust,
25    type_level::{impl_enum::Implemented, trait_marker},
26};
27
28use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
29
30// #[cfg(test)]
31#[cfg(all(test, not(feature = "only_new_tests")))]
32mod tests;
33
34/// A generic type for all ffi-safe non-exhaustive enums.
35///
36/// This type allows adding variants to enums it wraps in ABI compatible versions of a library.
37///
38/// # Generic parameters
39///
40/// ###  `E`
41///
42/// This is the enum that this was constructed from,
43/// and can be unwrapped back into if it's one of the valid variants in this context.
44///
45/// ###  `S`
46///
47/// The storage type,used to store the enum opaquely.
48///
49/// This has to be at least the size and alignment of the wrapped enum.
50///
51/// This is necessary because:
52///
53/// - The compiler assumes that an enum cannot be a variant outside the ones it sees.
54///
55/// - To give some flexibility to grow the enum in semver compatible versions of a library.
56///
57/// ###  `I`
58///
59/// The interface of the enum(it implements [`InterfaceType`](crate::InterfaceType)),
60/// determining which traits are required when constructing `NonExhaustive<>`
61/// and which are available afterwards.
62///
63/// # Examples
64///
65/// ### Error type
66///
67/// Say that we define an error type for a library.
68///
69///
70/// Version 1.0.
71/// ```
72/// use abi_stable::{
73///     nonexhaustive_enum::{NonExhaustive, NonExhaustiveFor},
74///     sabi_trait,
75///     std_types::RString,
76///     StableAbi,
77/// };
78///
79/// #[repr(u8)]
80/// #[derive(StableAbi, Debug, Clone, PartialEq)]
81/// #[sabi(kind(WithNonExhaustive(
82///     size = [usize;8],
83///     traits(Debug, Clone, PartialEq),
84/// )))]
85/// #[non_exhaustive]
86/// pub enum Error {
87///     CouldNotFindItem {
88///         name: RString,
89///     },
90///     OutOfStock {
91///         id: usize,
92///         name: RString,
93///     },
94/// }
95///
96/// fn returns_could_not_find_item(name: RString) -> NonExhaustiveFor<Error> {
97///     let e = Error::CouldNotFindItem { name };
98///     NonExhaustive::new(e)
99/// }
100///
101/// fn returns_out_of_stock(id: usize, name: RString) -> NonExhaustiveFor<Error> {
102///     let e = Error::OutOfStock { id, name };
103///     NonExhaustive::new(e)
104/// }
105///
106/// ```
107///
108/// Then in 1.1 we add another error variant,returned only by new library functions.
109///
110/// ```
111/// use abi_stable::{
112///     nonexhaustive_enum::{NonExhaustive, NonExhaustiveFor},
113///     sabi_trait,
114///     std_types::RString,
115///     StableAbi,
116/// };
117///
118/// #[repr(u8)]
119/// #[derive(StableAbi, Debug, Clone, PartialEq)]
120/// #[sabi(kind(WithNonExhaustive(
121///     size = [usize;8],
122///     traits(Debug, Clone, PartialEq),
123/// )))]
124/// #[non_exhaustive]
125/// pub enum Error {
126///     CouldNotFindItem {
127///         name: RString,
128///     },
129///     OutOfStock {
130///         id: usize,
131///         name: RString,
132///     },
133///     InvalidItemId {
134///         id: usize,
135///     },
136/// }
137///
138/// fn returns_invalid_item_id() -> NonExhaustiveFor<Error> {
139///     NonExhaustive::new(Error::InvalidItemId { id: 100 })
140/// }
141///
142/// ```
143///
144/// If a library user attempted to unwrap `Error::InvalidItemId`
145/// (using NonExhaustive::as_enum/as_enum_mut/into_enum)
146/// with the 1.0 version of `Error` they would get an `Err(..)` back.
147///
148///
149/// ### Static enums
150///
151/// This example demonstrates putting a nonexhaustive enum in a static.
152///
153/// ```rust
154/// use abi_stable::{
155///     nonexhaustive_enum::{NonExhaustive, NonExhaustiveFor},
156///     std_types::RString,
157///     rstr, StableAbi,
158/// };
159///
160/// static AA: NonExhaustiveFor<Foo> = NonExhaustive::new(Foo::A);
161///
162/// static BB: NonExhaustiveFor<Foo> = NonExhaustive::new(Foo::B(2));
163///
164/// let cc = NonExhaustive::new(Foo::C {name: "hello".into()});
165///
166/// assert_eq!(AA, Foo::A);
167/// assert_eq!(BB, Foo::B(2));
168/// assert_eq!(cc, Foo::C {name: RString::from("hello")});
169///
170///
171/// #[repr(u8)]
172/// #[derive(StableAbi, Debug, PartialEq, Eq)]
173/// #[sabi(kind(WithNonExhaustive(
174///     size = 64,
175///     traits(Debug, PartialEq, Eq)
176/// )))]
177/// pub enum Foo {
178///     A,
179///     B(i8),
180///     C { name: RString },
181/// }
182///
183/// ```
184///
185///
186#[repr(C)]
187#[derive(StableAbi)]
188#[sabi(
189    //debug_print,
190    not_stableabi(E,S,I),
191    bound(NonExhaustiveVtable_Ref<E,S,I>:StableAbi),
192    bound(E: NonExhaustiveMarker<S>),
193    bound(I: InterfaceType),
194    extra_checks = <I as MakeRequiredTraits>::MAKE,
195    phantom_type_param = <E as NonExhaustiveMarker<S>>::Marker,
196)]
197pub struct NonExhaustive<E, S, I> {
198    // This is an opaque field since we only care about its size and alignment
199    #[sabi(unsafe_opaque_field)]
200    fill: ScratchSpace<E, S>,
201    vtable: NonExhaustiveVtable_Ref<E, S, I>,
202    _marker: PhantomData<()>,
203}
204
205/// The type of a `NonExhaustive` wrapping the enum `E`,
206/// using `E`'s  default storage and interface.
207pub type NonExhaustiveFor<E> =
208    NonExhaustive<E, <E as GetEnumInfo>::DefaultStorage, <E as GetEnumInfo>::DefaultInterface>;
209
210/// The type of a `NonExhaustive<>` wrapping the enum E,
211/// using the `E`'s  default storage and a custom interface.
212pub type NonExhaustiveWI<E, I> = NonExhaustive<E, <E as GetEnumInfo>::DefaultStorage, I>;
213
214/// The type of a `NonExhaustive<>` wrapping the enum E,
215/// using a custom storage and the `E`'s default interface.
216pub type NonExhaustiveWS<E, S> = NonExhaustive<E, S, <E as GetEnumInfo>::DefaultInterface>;
217
218impl<E, S, I> NonExhaustive<E, S, I> {
219    /// Constructs a `NonExhaustive<>` from `value` using its default interface and storage.
220    ///
221    /// # Panic
222    ///
223    /// This panics if the storage has an alignment or size smaller than that of `E`.
224    #[inline]
225    pub const fn new(value: E) -> Self
226    where
227        E: GetVTable<S, I> + GetEnumInfo<DefaultStorage = S, DefaultInterface = I>,
228    {
229        NonExhaustive::with_storage_and_interface(value)
230    }
231
232    /// Constructs a `NonExhaustive<>` from `value` using its default storage
233    /// and a custom interface.
234    ///
235    /// # Panic
236    ///
237    /// This panics if the storage has an alignment or size smaller than that of `E`.
238    #[inline]
239    pub const fn with_interface(value: E) -> Self
240    where
241        E: GetVTable<S, I> + GetEnumInfo<DefaultStorage = S>,
242    {
243        NonExhaustive::with_storage_and_interface(value)
244    }
245
246    /// Constructs a `NonExhaustive<>` from `value` using its default interface
247    /// and a custom storage.
248    ///
249    /// # Panic
250    ///
251    /// This panics if the storage has an alignment or size smaller than that of `E`.
252    #[inline]
253    pub const fn with_storage(value: E) -> Self
254    where
255        E: GetVTable<S, I> + GetEnumInfo<DefaultInterface = I>,
256    {
257        NonExhaustive::with_storage_and_interface(value)
258    }
259
260    /// Constructs a `NonExhaustive<>` from `value` using both a custom interface and storage.
261    ///
262    /// # Panic
263    ///
264    /// This panics if the storage has an alignment or size smaller than that of `E`.
265    #[inline]
266    pub const fn with_storage_and_interface(value: E) -> Self
267    where
268        E: GetVTable<S, I>,
269    {
270        unsafe { NonExhaustive::with_vtable(value, E::VTABLE) }
271    }
272
273    #[track_caller]
274    pub(super) const unsafe fn with_vtable(
275        value: E,
276        vtable: NonExhaustiveVtable_Ref<E, S, I>,
277    ) -> Self {
278        // `ScratchSpace::new` is what asserts that the enum is
279        // the correct size and alignment
280        Self {
281            fill: ScratchSpace::<E, S>::new(value),
282            vtable,
283            _marker: PhantomData,
284        }
285    }
286}
287
288impl<E, S, I> NonExhaustive<E, S, I>
289where
290    E: GetEnumInfo,
291{
292    /// wraps a reference to this `NonExhaustive<>` into a reference to the original enum.
293    ///
294    /// # Errors
295    ///
296    /// This returns an error if the wrapped enum is of a variant that is
297    /// not valid in this context.
298    ///
299    /// # Example
300    ///
301    /// This shows how some `NonExhaustive<enum>` can be unwrapped, and others cannot.<br>
302    /// That enum comes from a newer version of the library than this knows.
303    ///
304    /// ```
305    /// use abi_stable::nonexhaustive_enum::doc_enums::example_2::{
306    ///     new_a, new_b, new_c, Foo,
307    /// };
308    ///
309    /// assert_eq!(new_a().as_enum().ok(), Some(&Foo::A));
310    /// assert_eq!(new_b(10).as_enum().ok(), Some(&Foo::B(10)));
311    /// assert_eq!(new_b(77).as_enum().ok(), Some(&Foo::B(77)));
312    /// assert_eq!(new_c().as_enum().ok(), None);
313    ///
314    ///
315    /// ```
316    ///
317    pub fn as_enum(&self) -> Result<&E, UnwrapEnumError<&Self>> {
318        let discriminant = self.get_discriminant();
319        if E::is_valid_discriminant(discriminant) {
320            unsafe { Ok(&*(&self.fill as *const ScratchSpace<E, S> as *const E)) }
321        } else {
322            Err(UnwrapEnumError::new(self))
323        }
324    }
325
326    /// Unwraps a mutable reference to this `NonExhaustive<>` into a
327    /// mutable reference to the original enum.
328    ///
329    /// # Errors
330    ///
331    /// This returns an error if the wrapped enum is of a variant that is
332    /// not valid in this context.
333    ///
334    /// # Example
335    ///
336    /// This shows how some `NonExhaustive<enum>` can be unwrapped, and others cannot.<br>
337    /// That enum comes from a newer version of the library than this knows.
338    ///
339    /// ```
340    /// use abi_stable::nonexhaustive_enum::doc_enums::example_1::{
341    ///     new_a, new_b, new_c, Foo,
342    /// };
343    ///
344    /// assert_eq!(new_a().as_enum_mut().ok(), Some(&mut Foo::A));
345    /// assert_eq!(new_b(10).as_enum_mut().ok(), None);
346    /// assert_eq!(new_b(77).as_enum_mut().ok(), None);
347    /// assert_eq!(new_c().as_enum_mut().ok(), None);
348    ///
349    /// ```
350    pub fn as_enum_mut(&mut self) -> Result<&mut E, UnwrapEnumError<&mut Self>>
351    where
352        E: GetVTable<S, I>,
353    {
354        let discriminant = self.get_discriminant();
355        if E::is_valid_discriminant(discriminant) {
356            // Must update the vtable every time as_enum_mut is called,
357            // because if the enum is replaced with a variant with a discriminant
358            // outside the valid range for the functions in the vtable,
359            // it would be undefined behavior to call those functions.
360            self.vtable = E::VTABLE;
361            unsafe { Ok(&mut *(&mut self.fill as *mut ScratchSpace<E, S> as *mut E)) }
362        } else {
363            Err(UnwrapEnumError::new(self))
364        }
365    }
366
367    /// Unwraps this `NonExhaustive<>` into the original enum.
368    ///
369    /// # Errors
370    ///
371    /// This returns an error if the wrapped enum is of a variant that is
372    /// not valid in this context.
373    ///
374    /// # Example
375    ///
376    /// This shows how some `NonExhaustive<enum>` can be unwrapped, and others cannot.<br>
377    /// That enum comes from a newer version of the library than this knows.
378    ///
379    /// ```
380    /// use abi_stable::nonexhaustive_enum::doc_enums::example_2::{
381    ///     new_a, new_b, new_c, Foo,
382    /// };
383    ///
384    /// assert_eq!(new_a().into_enum().ok(), Some(Foo::A));
385    /// assert_eq!(new_b(10).into_enum().ok(), Some(Foo::B(10)));
386    /// assert_eq!(new_b(77).into_enum().ok(), Some(Foo::B(77)));
387    /// assert_eq!(new_c().into_enum().ok(), None);
388    ///
389    /// ```
390    pub fn into_enum(self) -> Result<E, UnwrapEnumError<Self>> {
391        let discriminant = self.get_discriminant();
392        if E::is_valid_discriminant(discriminant) {
393            let this = ManuallyDrop::new(self);
394            unsafe { Ok((&this.fill as *const ScratchSpace<E, S> as *const E).read()) }
395        } else {
396            Err(UnwrapEnumError::new(self))
397        }
398    }
399
400    /// Returns whether the discriminant of this enum is valid in this context.
401    ///
402    /// The only way for it to be invalid is if the dynamic library is a
403    /// newer version than this knows.
404    #[inline]
405    pub fn is_valid_discriminant(&self) -> bool {
406        E::is_valid_discriminant(self.get_discriminant())
407    }
408
409    /// Gets the value of the discriminant of the enum.
410    #[inline]
411    pub const fn get_discriminant(&self) -> E::Discriminant {
412        unsafe { *(&self.fill as *const ScratchSpace<E, S> as *const E::Discriminant) }
413    }
414}
415
416impl<E, S, I> NonExhaustive<E, S, I> {
417    /// Transmute this `NonExhaustive<E,S,I>` into `NonExhaustive<F,S,I>`,
418    /// changing the type of the enum it wraps.
419    ///
420    /// # Safety
421    ///
422    /// This has the same safety requirements that `std::mem::transmute` has.
423    ///
424    /// # Panics
425    ///
426    /// This panics if the storage has an alignment or size smaller than that of `F`.
427    ///
428    ///
429    pub const unsafe fn transmute_enum<F>(self) -> NonExhaustive<F, S, I> {
430        assert_correct_storage::<F, S>(AssertCsArgs::UNKNOWN);
431        unsafe { const_transmute!(NonExhaustive<E, S, I>, NonExhaustive<F, S, I>, self) }
432    }
433
434    /// Transmute this `&NonExhaustive<E,S,I>` into `&NonExhaustive<F,S,I>`,
435    /// changing the type of the enum it wraps.
436    ///
437    /// # Safety
438    ///
439    /// This has the same safety requirements that `std::mem::transmute` has.
440    ///
441    /// # Panics
442    ///
443    /// This panics if the storage has an alignment or size smaller than that of `F`.
444    pub const unsafe fn transmute_enum_ref<F>(&self) -> &NonExhaustive<F, S, I> {
445        assert_correct_storage::<F, S>(AssertCsArgs::UNKNOWN);
446        unsafe { &*(self as *const Self as *const _) }
447    }
448
449    /// Transmute this `&mut NonExhaustive<E,S,I>` into `&mut NonExhaustive<F,S,I>`,
450    /// changing the type of the enum it wraps.
451    ///
452    /// # Safety
453    ///
454    /// This has the same safety requirements that `std::mem::transmute` has.
455    ///
456    /// # Panics
457    ///
458    /// This panics if the storage has an alignment or size smaller than that of `F`.
459    pub unsafe fn transmute_enum_mut<F>(&mut self) -> &mut NonExhaustive<F, S, I> {
460        assert_correct_storage::<F, S>(AssertCsArgs::UNKNOWN);
461        unsafe { &mut *(self as *mut Self as *mut _) }
462    }
463
464    /// Transmute this pointer to a `NonExhaustive<E,S,I>` into
465    /// a pointer (of the same kind) to a `NonExhaustive<F,S,I>`,
466    /// changing the type of the enum it wraps.
467    ///
468    /// # Safety
469    ///
470    /// This has the same safety requirements that
471    /// `abi_stable::pointer_traits::TransmuteElement::transmute_element` has.
472    ///
473    /// # Panics
474    ///
475    /// This panics if the storage has an alignment or size smaller than that of `F`.
476    ///
477    pub unsafe fn transmute_enum_ptr<P, F>(this: P) -> P::TransmutedPtr
478    where
479        P: Deref<Target = Self>,
480        P: CanTransmuteElement<NonExhaustive<F, S, I>>,
481    {
482        assert_correct_storage::<F, S>(AssertCsArgs::UNKNOWN);
483        unsafe { this.transmute_element::<NonExhaustive<F, S, I>>() }
484    }
485
486    /// Gets a reference to the vtable of this `NonExhaustive<>`.
487    pub(crate) const fn vtable(&self) -> NonExhaustiveVtable_Ref<E, S, I> {
488        self.vtable
489    }
490
491    const fn sabi_erased_ref(&self) -> RRef<'_, ErasedObject> {
492        unsafe { RRef::from_raw(&self.fill as *const ScratchSpace<E, S> as *const ErasedObject) }
493    }
494
495    const fn as_erased_ref(&self) -> RRef<'_, ErasedObject> {
496        unsafe { RRef::from_raw(self as *const Self as *const ErasedObject) }
497    }
498
499    fn sabi_erased_mut(&mut self) -> RMut<'_, ErasedObject> {
500        unsafe { RMut::from_raw(&mut self.fill as *mut ScratchSpace<E, S> as *mut ErasedObject) }
501    }
502}
503
504impl<E, S, I> Clone for NonExhaustive<E, S, I>
505where
506    I: InterfaceType<Clone = Implemented<trait_marker::Clone>>,
507{
508    fn clone(&self) -> Self {
509        unsafe { self.vtable().clone_()(self.sabi_erased_ref(), self.vtable) }
510    }
511}
512
513impl<E, S, I> Display for NonExhaustive<E, S, I>
514where
515    I: InterfaceType<Display = Implemented<trait_marker::Display>>,
516{
517    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
518        unsafe {
519            c_functions::adapt_std_fmt::<ErasedObject>(
520                self.sabi_erased_ref(),
521                self.vtable().display(),
522                f,
523            )
524        }
525    }
526}
527
528impl<E, S, I> Debug for NonExhaustive<E, S, I>
529where
530    I: InterfaceType<Debug = Implemented<trait_marker::Debug>>,
531{
532    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533        unsafe {
534            c_functions::adapt_std_fmt::<ErasedObject>(
535                self.sabi_erased_ref(),
536                self.vtable().debug(),
537                f,
538            )
539        }
540    }
541}
542
543impl<E, S, I> Eq for NonExhaustive<E, S, I>
544where
545    Self: PartialEq,
546    I: InterfaceType<Eq = Implemented<trait_marker::Eq>>,
547{
548}
549
550impl<E, S, I1, I2> PartialEq<NonExhaustive<E, S, I2>> for NonExhaustive<E, S, I1>
551where
552    I1: InterfaceType<PartialEq = Implemented<trait_marker::PartialEq>>,
553{
554    fn eq(&self, other: &NonExhaustive<E, S, I2>) -> bool {
555        unsafe { self.vtable().partial_eq()(self.sabi_erased_ref(), other.as_erased_ref()) }
556    }
557}
558
559impl<E, S, I> Ord for NonExhaustive<E, S, I>
560where
561    I: InterfaceType<Ord = Implemented<trait_marker::Ord>>,
562    Self: PartialOrd + Eq,
563{
564    fn cmp(&self, other: &Self) -> Ordering {
565        unsafe { self.vtable().cmp()(self.sabi_erased_ref(), other.as_erased_ref()).into() }
566    }
567}
568
569impl<E, S, I1, I2> PartialOrd<NonExhaustive<E, S, I2>> for NonExhaustive<E, S, I1>
570where
571    I1: InterfaceType<PartialOrd = Implemented<trait_marker::PartialOrd>>,
572    Self: PartialEq<NonExhaustive<E, S, I2>>,
573{
574    fn partial_cmp(&self, other: &NonExhaustive<E, S, I2>) -> Option<Ordering> {
575        unsafe {
576            self.vtable().partial_cmp()(self.sabi_erased_ref(), other.as_erased_ref())
577                .map(IntoReprRust::into_rust)
578                .into()
579        }
580    }
581}
582
583/////////////////////
584
585impl<E, S, I> PartialOrd<E> for NonExhaustive<E, S, I>
586where
587    E: GetEnumInfo + PartialOrd,
588    I: InterfaceType<PartialOrd = Implemented<trait_marker::PartialOrd>>,
589    Self: PartialEq<E>,
590{
591    fn partial_cmp(&self, other: &E) -> Option<Ordering> {
592        match self.as_enum() {
593            Ok(this) => this.partial_cmp(other),
594            Err(_) => Some(Ordering::Greater),
595        }
596    }
597}
598
599impl<E, S, I> PartialEq<E> for NonExhaustive<E, S, I>
600where
601    E: GetEnumInfo + PartialEq,
602    I: InterfaceType<PartialEq = Implemented<trait_marker::PartialEq>>,
603{
604    fn eq(&self, other: &E) -> bool {
605        match self.as_enum() {
606            Ok(this) => this == other,
607            Err(_) => false,
608        }
609    }
610}
611
612/////////////////////
613
614impl<E, S, I> NonExhaustive<E, S, I>
615where
616    E: GetEnumInfo,
617{
618    /// It serializes a `NonExhaustive<_>` into a proxy.
619    pub fn serialize_into_proxy(&self) -> Result<I::Proxy, RBoxError>
620    where
621        I: InterfaceType<Serialize = Implemented<trait_marker::Serialize>>,
622        I: SerializeEnum<E>,
623    {
624        unsafe { self.vtable().serialize()(self.as_erased_ref()).into_result() }
625    }
626
627    /// Deserializes a `NonExhaustive<_>` from a proxy.
628    pub fn deserialize_from_proxy<'borr>(proxy: I::Proxy) -> Result<Self, RBoxError>
629    where
630        I: InterfaceType<Deserialize = Implemented<trait_marker::Deserialize>>,
631        I: DeserializeEnum<'borr, Self>,
632        I::Proxy: 'borr,
633    {
634        I::deserialize_enum(proxy)
635    }
636}
637
638/// First it serializes a `NonExhaustive<_>` into a proxy,then it serializes that proxy.
639impl<E, S, I> Serialize for NonExhaustive<E, S, I>
640where
641    I: InterfaceType<Serialize = Implemented<trait_marker::Serialize>>,
642    I: SerializeEnum<E>,
643    I::Proxy: Serialize,
644{
645    fn serialize<Z>(&self, serializer: Z) -> Result<Z::Ok, Z::Error>
646    where
647        Z: Serializer,
648    {
649        unsafe {
650            self.vtable().serialize()(self.as_erased_ref())
651                .into_result()
652                .map_err(ser::Error::custom)?
653                .serialize(serializer)
654        }
655    }
656}
657
658/// First it Deserializes a string,then it deserializes into a
659/// `NonExhaustive<_>`,by using `<I as DeserializeEnum>::deserialize_enum`.
660impl<'de, E, S, I> Deserialize<'de> for NonExhaustive<E, S, I>
661where
662    E: 'de + GetVTable<S, I>,
663    S: 'de,
664    I: 'de + InterfaceType<Deserialize = Implemented<trait_marker::Deserialize>>,
665    I: DeserializeEnum<'de, Self>,
666    <I as DeserializeEnum<'de, Self>>::Proxy: Deserialize<'de>,
667{
668    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
669    where
670        D: Deserializer<'de>,
671    {
672        let s =
673            <<I as DeserializeEnum<'de, Self>>::Proxy as Deserialize>::deserialize(deserializer)?;
674
675        I::deserialize_enum(s).map_err(de::Error::custom)
676    }
677}
678
679/////////////////////
680
681impl<E, S, I> Hash for NonExhaustive<E, S, I>
682where
683    I: InterfaceType<Hash = Implemented<trait_marker::Hash>>,
684{
685    fn hash<H>(&self, state: &mut H)
686    where
687        H: Hasher,
688    {
689        unsafe { self.vtable().hash()(self.sabi_erased_ref(), HasherObject::new(state)) }
690    }
691}
692
693impl<E, S, I> std::error::Error for NonExhaustive<E, S, I> where
694    I: InterfaceType<
695        Debug = Implemented<trait_marker::Debug>,
696        Display = Implemented<trait_marker::Display>,
697        Error = Implemented<trait_marker::Error>,
698    >
699{
700}
701
702/////////////////////
703
704impl<E, S, I> Drop for NonExhaustive<E, S, I> {
705    fn drop(&mut self) {
706        let drop = self.vtable()._sabi_drop();
707
708        unsafe {
709            drop(self.sabi_erased_mut());
710        }
711    }
712}
713
714///////////////////////////////////////////////////////////////////////////////
715
716/// Used to abstract over the reference-ness of [`NonExhaustive`] inside [`UnwrapEnumError`].
717pub trait NonExhaustiveSharedOps {
718    /// The type of the discriminant of the wrapped enum.
719    type Discriminant: ValidDiscriminant;
720
721    /// Gets the discriminant of the wrapped enum.
722    fn get_discriminant_(&self) -> Self::Discriminant;
723
724    /// Gets miscelaneous information about the wrapped enum
725    fn enum_info_(&self) -> &'static EnumInfo;
726}
727
728/// A struct storing the discriminant and `EnumInfo` of some enum.
729struct DiscrAndEnumInfo<E> {
730    discr: E,
731    enum_info: &'static EnumInfo,
732}
733
734impl<E> NonExhaustiveSharedOps for DiscrAndEnumInfo<E>
735where
736    E: ValidDiscriminant,
737{
738    type Discriminant = E;
739    fn get_discriminant_(&self) -> E {
740        self.discr
741    }
742    fn enum_info_(&self) -> &'static EnumInfo {
743        self.enum_info
744    }
745}
746
747macro_rules! impl_neso {
748    (
749        impl[$E:ident,$S:ident,$I:ident]
750    ) => {
751        type Discriminant = $E::Discriminant;
752
753        fn get_discriminant_(&self) -> $E::Discriminant {
754            self.get_discriminant()
755        }
756
757        fn enum_info_(&self) -> &'static EnumInfo {
758            self.vtable().enum_info()
759        }
760    };
761}
762
763impl<E, S, I> NonExhaustiveSharedOps for NonExhaustive<E, S, I>
764where
765    E: GetEnumInfo,
766{
767    impl_neso! { impl[E,S,I] }
768}
769
770impl<'a, E, S, I> NonExhaustiveSharedOps for &'a NonExhaustive<E, S, I>
771where
772    E: GetEnumInfo,
773{
774    impl_neso! { impl[E,S,I] }
775}
776
777impl<'a, E, S, I> NonExhaustiveSharedOps for &'a mut NonExhaustive<E, S, I>
778where
779    E: GetEnumInfo,
780{
781    impl_neso! { impl[E,S,I] }
782}
783
784///////////////////////////////////////////////////////////////////////////////
785
786/// An error for a situation where a `NonExhaustive<>` could not be unwrapped into the enum
787/// because the discriminant wasn't valid in this context
788/// (likely because it is from a newer version of the library).
789#[must_use]
790#[repr(transparent)]
791#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, StableAbi)]
792#[non_exhaustive]
793pub struct UnwrapEnumError<N> {
794    /// This field is either a `NonExhaustive<>` or a `DiscrAndEnumInfo<>`
795    pub non_exhaustive: N,
796}
797
798#[allow(clippy::missing_const_for_fn)]
799impl<N> UnwrapEnumError<N> {
800    /// Gets the `non_exhaustive` field.
801    #[must_use]
802    pub fn into_inner(self) -> N {
803        self.non_exhaustive
804    }
805
806    /// Converts this into a boxed error.
807    pub fn into_boxed(self) -> RBoxError
808    where
809        N: NonExhaustiveSharedOps,
810    {
811        let x = DiscrAndEnumInfo {
812            discr: self.non_exhaustive.get_discriminant_(),
813            enum_info: self.non_exhaustive.enum_info_(),
814        };
815        let x = UnwrapEnumError::new(x);
816        RBoxError::new(x)
817    }
818}
819
820impl<N> UnwrapEnumError<N> {
821    #[inline]
822    const fn new(non_exhaustive: N) -> Self {
823        Self { non_exhaustive }
824    }
825}
826
827impl<N> Display for UnwrapEnumError<N>
828where
829    N: NonExhaustiveSharedOps,
830{
831    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
832        write!(
833            f,
834            "Could not unwrap NonExhaustive into '{}'.\n\
835             Because its discriminant was {:?} .",
836            self.non_exhaustive.enum_info_().type_name(),
837            self.non_exhaustive.get_discriminant_(),
838        )
839    }
840}
841
842impl<N> Debug for UnwrapEnumError<N>
843where
844    N: NonExhaustiveSharedOps,
845{
846    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847        f.debug_struct("UnwrapEnumError")
848            .field("non_exhaustive", &"<opaque>")
849            .field("discriminant", &self.non_exhaustive.get_discriminant_())
850            .field("enum_info", &self.non_exhaustive.enum_info_())
851            .finish()
852    }
853}
854
855impl<N> From<UnwrapEnumError<N>> for RBoxError
856where
857    N: NonExhaustiveSharedOps,
858{
859    fn from(uee: UnwrapEnumError<N>) -> RBoxError {
860        uee.into_boxed()
861    }
862}
863
864impl<N> std::error::Error for UnwrapEnumError<N> where N: NonExhaustiveSharedOps {}