1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Contains types and traits for nonexhaustive enums.
//!
//! The most important type here is [NonExhaustive](./nonexhaustive/struct.NonExhaustive.html),
//! which allows passing an enum which used the
//! `#[derive(StableAbi)] #[sabi(kind(WithNonExhaustive(...)))]`
//! attributes through ffi.
//!

#[doc(hidden)]
pub mod doc_enums;

#[cfg(any(feature = "testing", feature = "nonexhaustive_examples"))]
pub mod examples;

pub(crate) mod alt_c_functions;
pub(crate) mod nonexhaustive;
pub(crate) mod traits;
pub(crate) mod vtable;

pub use self::{
    nonexhaustive::{
        NonExhaustive, NonExhaustiveFor, NonExhaustiveSharedOps, NonExhaustiveWI, NonExhaustiveWS,
        UnwrapEnumError,
    },
    traits::{
        DeserializeEnum, EnumInfo, GetEnumInfo, NonExhaustiveMarker, SerializeEnum,
        ValidDiscriminant,
    },
    vtable::GetVTable,
};

pub(crate) use self::traits::GetSerializeEnumProxy;

/////////////////////////////////////////////////////////////

/// Asserts that the size and alignment of an enum are valid for its default storage.
#[track_caller]
pub fn assert_correct_default_storage<E>()
where
    E: GetEnumInfo,
{
    assert_correct_storage::<E, E::DefaultStorage>(AssertCsArgs {
        enum_ty: std::any::type_name::<E>(),
        storage_ty: std::any::type_name::<E::DefaultStorage>(),
    })
}

/// Arguments for [`assert_correct_storage`]
pub struct AssertCsArgs {
    /// The stringified type name of the enum.
    pub enum_ty: &'static str,
    /// The stringified type name of the storage.
    pub storage_ty: &'static str,
}

impl AssertCsArgs {
    /// Constant where both types are unknown.
    pub const UNKNOWN: Self = Self {
        enum_ty: "<unknown>",
        storage_ty: "<unknown>",
    };
}

/// Asserts that the size and alignment of an enum aree valid for this storage.
///
/// To make this function a `const fn`,
/// the names of the `Enum` and `Storage` types must be passed separately.
#[track_caller]
pub const fn assert_correct_storage<Enum, Storage>(args: AssertCsArgs) {
    #[derive(Debug)]
    #[allow(dead_code)]
    struct TypeAndStorageLayout {
        enum_: &'static str,
        enum_size: usize,
        enum_alignment: usize,
        storage_: &'static str,
        storage_size: usize,
        storage_alignment: usize,
    }

    #[track_caller]
    const fn inner(lay: TypeAndStorageLayout) {
        let msg = match (
            lay.enum_alignment <= lay.storage_alignment,
            lay.enum_size <= lay.storage_size,
        ) {
            (false, false) => {
                "The alignment and size of the storage is smaller than the contained type"
            }
            (false, true) => "The alignment of the storage is lower than the contained type",
            (true, false) => "The size of the storage is smaller than the contained type",
            (true, true) => return,
        };

        const_panic::concat_panic!(
            "\n",
            display: msg,
            ":\n",
            "\tenum_: ",
            lay.enum_,
            "\n",
            "\tenum_size: ",
            lay.enum_size,
            "\n",
            "\tenum_alignment: ",
            lay.enum_alignment,
            "\n",
            "\tstorage_: ",
            lay.storage_,
            "\n",
            "\tstorage_size: ",
            lay.storage_size,
            "\n",
            "\tstorage_alignment: ",
            lay.storage_alignment,
            "\n",
        )
    }

    inner(TypeAndStorageLayout {
        enum_: args.enum_ty,
        enum_size: std::mem::size_of::<Enum>(),
        enum_alignment: std::mem::align_of::<Enum>(),
        storage_: args.storage_ty,
        storage_size: std::mem::size_of::<Storage>(),
        storage_alignment: std::mem::align_of::<Storage>(),
    })
}