Skip to main content

object_rainbow/
enumkind.rs

1use crate::*;
2
3/// Discriminant arithmetic glue to make `#[derive(Enum)]` function.
4pub trait UsizeTag: Sized {
5    /// Used by [`EnumTag::from_const`].
6    ///
7    /// ## Panics
8    ///
9    /// Panics on out-of-bounds.
10    fn from_usize(n: usize) -> Self;
11    /// Convert a trusted value to [`usize`].
12    fn to_usize(&self) -> usize;
13    /// Convert an untrusted value to [`usize`].
14    fn try_to_usize(&self) -> Option<usize>;
15}
16
17/// [`EnumKind::Tag`].
18#[derive(
19    ToOutput,
20    InlineOutput,
21    ListHashes,
22    Topological,
23    Tagged,
24    ParseAsInline,
25    Size,
26    MaybeHasNiche,
27    PartialEq,
28    Eq,
29    PartialOrd,
30    Ord,
31    ByteOrd,
32)]
33pub struct EnumTag<T, const MAX: usize>(T);
34
35impl<T: UsizeTag, const MAX: usize> UsizeTag for EnumTag<T, MAX> {
36    fn from_usize(n: usize) -> Self {
37        assert!(n < MAX);
38        Self(UsizeTag::from_usize(n))
39    }
40
41    fn to_usize(&self) -> usize {
42        self.0.to_usize()
43    }
44
45    fn try_to_usize(&self) -> Option<usize> {
46        self.0.try_to_usize()
47    }
48}
49
50impl<T: UsizeTag, const MAX: usize> EnumTag<T, MAX> {
51    /// Inherent alias of [`UsizeTag::to_usize`].
52    pub fn to_usize(&self) -> usize {
53        self.0.to_usize()
54    }
55
56    /// Generate the tag from a statically known index.
57    pub fn from_const<const N: usize>() -> Self {
58        assert!(N < MAX);
59        Self(UsizeTag::from_usize(N))
60    }
61}
62
63impl<T: ParseInline<I> + UsizeTag, I: ParseInput, const MAX: usize> ParseInline<I>
64    for EnumTag<T, MAX>
65{
66    fn parse_inline(input: &mut I) -> crate::Result<Self> {
67        let n_raw = T::parse_inline(input)?;
68        let n: Option<usize> = n_raw.try_to_usize();
69        if let Some(n) = n
70            && n < MAX
71        {
72            return Ok(Self(n_raw));
73        }
74        Err(Error::DiscriminantOverflow)
75    }
76}
77
78/// An [`Inline`] identifying variants of an [`Enum`].
79pub trait EnumKind: Copy {
80    /// Underlying [`Inline`]. Typically [`EnumTag`].
81    type Tag;
82    /// Get the underlying [`Inline`].
83    fn to_tag(self) -> Self::Tag;
84    /// Convert from an [`EnumTag`] assuming the value is within bounds, such that otherwise we have
85    /// caught their violation during parsing of that tag.
86    ///
87    /// Panics on out-of-bounds.
88    fn from_tag(tag: Self::Tag) -> Self;
89}
90
91/// `enum`.
92pub trait Enum {
93    /// Discriminant uniquely identifying this `enum`'s variants.
94    type Kind: EnumKind;
95    /// Get the [`EnumKind`] of this variant.
96    fn kind(&self) -> Self::Kind;
97}
98
99/// [`Enum`]-specific [`Parse`].
100pub trait EnumParse<I: ParseInput>: Enum + Parse<I> {
101    /// Given an already-parsed [`EnumKind`], parse the rest of a [`Parse`] [`Enum`].
102    fn enum_parse(kind: Self::Kind, input: I) -> crate::Result<Self>;
103    /// For implementing [`Parse::parse`].
104    fn parse_as_enum(mut input: I) -> crate::Result<Self>
105    where
106        <Self::Kind as EnumKind>::Tag: ParseInline<I>,
107    {
108        Self::enum_parse(Self::Kind::from_tag(input.parse_inline()?), input)
109    }
110}
111
112/// [`Enum`]-specific [`ParseInline`].
113pub trait EnumParseInline<I: ParseInput>: Enum + ParseInline<I> {
114    /// Given an already-parsed [`EnumKind`], parse the rest of a [`ParseInline`] [`Enum`].
115    fn enum_parse_inline(kind: Self::Kind, input: &mut I) -> crate::Result<Self>;
116    /// For implementing [`ParseInline::parse_inline`].
117    fn parse_as_inline_enum(input: &mut I) -> crate::Result<Self>
118    where
119        <Self::Kind as EnumKind>::Tag: ParseInline<I>,
120    {
121        Self::enum_parse_inline(Self::Kind::from_tag(input.parse_inline()?), input)
122    }
123}