fend_core/num/
base.rs

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 with 0b prefix
15	Binary,
16	/// Octal with 0o prefix
17	Octal,
18	/// Hex with 0x prefix
19	Hex,
20	/// Custom base between 2 and 36 (inclusive), written as base#number
21	Custom(u8),
22	/// Plain (no prefix)
23	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}