use std::fmt;
use proc_macro2::TokenStream;
use quote::{quote, format_ident};
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub enum Type {
Bool,
Num(BitCount),
}
impl Type {
pub fn for_template(bit_count: u8) -> Result<Self, String> {
match bit_count {
8 | 16 | 32 | 64 | 128 => Ok(Self::Num(BitCount::new(bit_count)?)),
_ => Err(format!("Template width must be 8, 16, 32, 64, or 128, but was {bit_count}.")),
}
}
pub fn for_field(bit_count: u8, precision: Precision) -> Result<Self, String> {
match bit_count {
0 => Err("Fields cannot have zero bits.".into()),
1 => Ok(Self::Bool),
1..=128 if precision == Precision::Ux => Ok(Self::Num(BitCount::new(bit_count)?)),
2..=8 => Ok(Self::Num(BitCount::U8)),
9..=16 => Ok(Self::Num(BitCount::U16)),
17..=32 => Ok(Self::Num(BitCount::U32)),
33..=64 => Ok(Self::Num(BitCount::U64)),
65..=128 => Ok(Self::Num(BitCount::U128)),
129..=u8::MAX => Err("Integers larger than u128 are not supported.".into()),
}
}
pub fn parse(mut text: String) -> Result<Self, String> {
if &text == "bool" {
return Ok(Type::Bool);
}
let u = text.remove(0);
if u != 'u' {
return Err("Type must be bool or start with 'u'.".into());
}
let count: u8 = text.parse()
.map_err(|err: std::num::ParseIntError| err.to_string())?;
Ok(Type::Num(BitCount::new(count)?))
}
pub const fn is_standard(self) -> bool {
match self {
Self::Bool => true,
Self::Num(n) => matches!(n.0, 8 | 16 | 32 | 64 | 128),
}
}
pub fn concat(self, other: Self) -> Result<Self, String> {
Self::for_field(self.bit_count() + other.bit_count(), Precision::Standard)
}
pub fn to_token_stream(self) -> TokenStream {
let ident = format_ident!("{}", self.to_string());
if self.is_standard() {
quote! { #ident }
} else {
quote! { ux::#ident }
}
}
pub const fn bit_count(self) -> u8 {
match self {
Self::Bool => 1,
Self::Num(n) => n.0,
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Bool => write!(f, "bool"),
Self::Num(n) => write!(f, "u{}", n.0),
}
}
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Precision {
Standard,
Ux,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct BitCount(u8);
impl BitCount {
pub const U8: Self = Self(8);
pub const U16: Self = Self(16);
pub const U32: Self = Self(32);
pub const U64: Self = Self(64);
pub const U128: Self = Self(128);
pub fn new(count: u8) -> Result<Self, String> {
match count {
0 => Err("u0 is not a valid type of integer. BitCount must be positive.".into()),
1..=128 => Ok(Self(count)),
129..=u8::MAX => Err("Integers larger than u128 are not supported.".into()),
}
}
}