Skip to main content

object_rainbow/
enumkind.rs

1use crate::*;
2
3pub trait UsizeTag: Sized {
4    /// Used by [`EnumTag::from_const`].
5    ///
6    /// ## Panics
7    ///
8    /// Panics on out-of-bounds.
9    fn from_usize(n: usize) -> Self;
10    fn to_usize(&self) -> usize;
11    fn try_to_usize(&self) -> Option<usize>;
12}
13
14impl UsizeTag for bool {
15    fn from_usize(n: usize) -> Self {
16        match n {
17            0 => false,
18            1 => true,
19            _ => panic!("out of bounds"),
20        }
21    }
22
23    fn to_usize(&self) -> usize {
24        *self as _
25    }
26
27    fn try_to_usize(&self) -> Option<usize> {
28        Some(self.to_usize())
29    }
30}
31
32#[derive(
33    ToOutput, InlineOutput, ListHashes, Topological, Tagged, ParseAsInline, Size, MaybeHasNiche,
34)]
35pub struct EnumTag<T, const MAX: usize>(T);
36
37impl<T: UsizeTag, const MAX: usize> UsizeTag for EnumTag<T, MAX> {
38    fn from_usize(n: usize) -> Self {
39        assert!(n < MAX);
40        Self(UsizeTag::from_usize(n))
41    }
42
43    fn to_usize(&self) -> usize {
44        self.0.to_usize()
45    }
46
47    fn try_to_usize(&self) -> Option<usize> {
48        self.0.try_to_usize()
49    }
50}
51
52impl<T: UsizeTag, const MAX: usize> EnumTag<T, MAX> {
53    pub fn to_usize(&self) -> usize {
54        self.0.to_usize()
55    }
56
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            if n < MAX {
71                return Ok(Self(n_raw));
72            }
73        }
74        Err(Error::DiscriminantOverflow)
75    }
76}
77
78pub trait EnumKind: Copy {
79    type Tag;
80    fn to_tag(self) -> Self::Tag;
81    fn from_tag(tag: Self::Tag) -> Self;
82}
83
84pub trait Enum {
85    type Kind: EnumKind;
86    fn kind(&self) -> Self::Kind;
87}
88
89pub trait EnumParse<I: ParseInput>: Enum + Parse<I> {
90    fn enum_parse(kind: Self::Kind, input: I) -> crate::Result<Self>;
91    fn parse_as_enum(mut input: I) -> crate::Result<Self>
92    where
93        <Self::Kind as EnumKind>::Tag: ParseInline<I>,
94    {
95        Self::enum_parse(Self::Kind::from_tag(input.parse_inline()?), input)
96    }
97}
98
99pub trait EnumParseInline<I: ParseInput>: Enum + ParseInline<I> {
100    fn enum_parse_inline(kind: Self::Kind, input: &mut I) -> crate::Result<Self>;
101    fn parse_as_inline_enum(input: &mut I) -> crate::Result<Self>
102    where
103        <Self::Kind as EnumKind>::Tag: ParseInline<I>,
104    {
105        Self::enum_parse_inline(Self::Kind::from_tag(input.parse_inline()?), input)
106    }
107}