1 use std::{fmt, io}; 2 3 use crate::{ 4 error::FendError, 5 result::FResult, 6 serialize::{Deserialize, Serialize}, 7 }; 8 9 #[derive(Copy, Clone, PartialEq, Eq)] 10 pub(crate) struct Base(BaseEnum); 11 12 #[derive(Copy, Clone, PartialEq, Eq)] 13 enum 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 26 impl Base { 27 pub(crate) const HEX: Self = Self(BaseEnum::Hex); 28 base_as_u8(self) -> u829 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 from_zero_based_prefix_char(ch: char) -> FResult<Self>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 from_plain_base(base: u8) -> FResult<Self>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 from_custom_base(base: u8) -> FResult<Self>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 write_prefix(self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>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 has_prefix(self) -> bool76 pub(crate) const fn has_prefix(self) -> bool { 77 !matches!(self.0, BaseEnum::Plain(_)) 78 } 79 digit_as_char(digit: u64) -> Option<char>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 serialize(self, write: &mut impl io::Write) -> FResult<()>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 deserialize(read: &mut impl io::Read) -> FResult<Self>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 151 impl Default for Base { default() -> Self152 fn default() -> Self { 153 Self(BaseEnum::Plain(10)) 154 } 155 } 156 157 impl fmt::Debug for Base { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result158 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 } 168