1use std::{fmt, io};
2
3use crate::{
4 error::FendError,
5 result::FResult,
6 serialize::{Deserialize, Serialize},
7};
8
9#[derive(Copy, Clone, PartialEq, Eq)]
10pub(crate) struct Base(BaseEnum);
11
12#[derive(Copy, Clone, PartialEq, Eq)]
13enum BaseEnum {
14 Binary,
16 Octal,
18 Hex,
20 Custom(u8),
22 Plain(u8),
24}
25
26impl Base {
27 pub(crate) const HEX: Self = Self(BaseEnum::Hex);
28
29 pub(crate) const fn base_as_u8(self) -> u8 {
30 match self.0 {
31 BaseEnum::Binary => 2,
32 BaseEnum::Octal => 8,
33 BaseEnum::Hex => 16,
34 BaseEnum::Custom(b) | BaseEnum::Plain(b) => b,
35 }
36 }
37
38 pub(crate) const fn from_zero_based_prefix_char(ch: char) -> FResult<Self> {
39 Ok(match ch {
40 'x' => Self(BaseEnum::Hex),
41 'o' => Self(BaseEnum::Octal),
42 'b' => Self(BaseEnum::Binary),
43 _ => return Err(FendError::InvalidBasePrefix),
44 })
45 }
46
47 pub(crate) const fn from_plain_base(base: u8) -> FResult<Self> {
48 if base < 2 {
49 return Err(FendError::BaseTooSmall);
50 } else if base > 36 {
51 return Err(FendError::BaseTooLarge);
52 }
53 Ok(Self(BaseEnum::Plain(base)))
54 }
55
56 pub(crate) const fn from_custom_base(base: u8) -> FResult<Self> {
57 if base < 2 {
58 return Err(FendError::BaseTooSmall);
59 } else if base > 36 {
60 return Err(FendError::BaseTooLarge);
61 }
62 Ok(Self(BaseEnum::Custom(base)))
63 }
64
65 pub(crate) fn write_prefix(self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
66 match self.0 {
67 BaseEnum::Binary => write!(f, "0b")?,
68 BaseEnum::Octal => write!(f, "0o")?,
69 BaseEnum::Hex => write!(f, "0x")?,
70 BaseEnum::Custom(b) => write!(f, "{b}#")?,
71 BaseEnum::Plain(_) => (),
72 }
73 Ok(())
74 }
75
76 pub(crate) const fn has_prefix(self) -> bool {
77 !matches!(self.0, BaseEnum::Plain(_))
78 }
79
80 pub(crate) const fn digit_as_char(digit: u64) -> Option<char> {
81 Some(match digit {
82 0 => '0',
83 1 => '1',
84 2 => '2',
85 3 => '3',
86 4 => '4',
87 5 => '5',
88 6 => '6',
89 7 => '7',
90 8 => '8',
91 9 => '9',
92 10 => 'a',
93 11 => 'b',
94 12 => 'c',
95 13 => 'd',
96 14 => 'e',
97 15 => 'f',
98 16 => 'g',
99 17 => 'h',
100 18 => 'i',
101 19 => 'j',
102 20 => 'k',
103 21 => 'l',
104 22 => 'm',
105 23 => 'n',
106 24 => 'o',
107 25 => 'p',
108 26 => 'q',
109 27 => 'r',
110 28 => 's',
111 29 => 't',
112 30 => 'u',
113 31 => 'v',
114 32 => 'w',
115 33 => 'x',
116 34 => 'y',
117 35 => 'z',
118 _ => return None,
119 })
120 }
121
122 pub(crate) fn serialize(self, write: &mut impl io::Write) -> FResult<()> {
123 match self.0 {
124 BaseEnum::Binary => 1u8.serialize(write)?,
125 BaseEnum::Octal => 2u8.serialize(write)?,
126 BaseEnum::Hex => 3u8.serialize(write)?,
127 BaseEnum::Custom(b) => {
128 4u8.serialize(write)?;
129 b.serialize(write)?;
130 }
131 BaseEnum::Plain(b) => {
132 5u8.serialize(write)?;
133 b.serialize(write)?;
134 }
135 }
136 Ok(())
137 }
138
139 pub(crate) fn deserialize(read: &mut impl io::Read) -> FResult<Self> {
140 Ok(Self(match u8::deserialize(read)? {
141 1 => BaseEnum::Binary,
142 2 => BaseEnum::Octal,
143 3 => BaseEnum::Hex,
144 4 => BaseEnum::Custom(u8::deserialize(read)?),
145 5 => BaseEnum::Plain(u8::deserialize(read)?),
146 _ => return Err(FendError::DeserializationError),
147 }))
148 }
149}
150
151impl Default for Base {
152 fn default() -> Self {
153 Self(BaseEnum::Plain(10))
154 }
155}
156
157impl fmt::Debug for Base {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 match self.0 {
160 BaseEnum::Binary => write!(f, "binary"),
161 BaseEnum::Octal => write!(f, "octal"),
162 BaseEnum::Hex => write!(f, "hex"),
163 BaseEnum::Custom(b) => write!(f, "base {b} (with prefix)"),
164 BaseEnum::Plain(b) => write!(f, "base {b}"),
165 }
166 }
167}