use err::{ChipNameError, CoreNameError, ParseVendorError};
mod chips;
mod cores;
pub mod err;
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[cfg_attr(test, derive(strum::EnumIter))]
#[non_exhaustive]
pub enum Vendor {
Intel,
Amd,
Centaur,
Cyrix,
Dmp,
Hygon,
Mcst,
Nsc,
Nexgen,
Rdc,
Rise,
Sis,
Transmeta,
Umc,
Via,
Zhaoxin,
}
impl core::fmt::Display for Vendor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let s = match self {
Self::Intel => "Intel",
Self::Amd => "AMD",
Self::Centaur => "Centaur",
Self::Cyrix => "Cyrix",
Self::Dmp => "DMP",
Self::Hygon => "Hygon",
Self::Mcst => "MCST",
Self::Nsc => "National Semiconductor",
Self::Nexgen => "NexGen",
Self::Rdc => "RDC",
Self::Rise => "Rise",
Self::Sis => "SiS",
Self::Transmeta => "Transmeta",
Self::Umc => "UMC",
Self::Via => "VIA",
Self::Zhaoxin => "Zhaoxin",
};
write!(f, "{s}")
}
}
impl core::str::FromStr for Vendor {
type Err = ParseVendorError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 12 {
return Err(ParseVendorError::InvalidLength(s.len()));
}
if !s.is_ascii() {
return Err(ParseVendorError::InvalidCharacters);
}
match s {
"GenuineIntel" | "GenuineIotel" => Ok(Self::Intel),
"AuthenticAMD" | "AMD ISBETTER" => Ok(Self::Amd),
"CentaurHauls" => Ok(Self::Centaur),
"CyrixInstead" => Ok(Self::Cyrix),
"Vortex86 SoC" => Ok(Self::Dmp),
"HygonGenuine" => Ok(Self::Hygon),
"E2K MACHINE " => Ok(Self::Mcst),
"NexGenDriven" => Ok(Self::Nexgen),
"Geode by NSC" => Ok(Self::Nsc),
"Genuine RDC" => Ok(Self::Rdc),
"RiseRiseRise" => Ok(Self::Rise),
"SiS SiS SiS " => Ok(Self::Sis),
"TransmetaCPU" | "GenuineTMx86" => Ok(Self::Transmeta),
"UMC UMC UMC " => Ok(Self::Umc),
"VIA VIA VIA " => Ok(Self::Via),
" Shanghai " => Ok(Self::Zhaoxin),
_ => s
.as_bytes()
.try_into()
.map_or(Err(ParseVendorError::InvalidLength(s.len())), |bytes| {
Err(ParseVendorError::NoMatch(bytes))
}),
}
}
}
pub const fn chip_name(
vendor: Vendor,
family: u8,
model: u8,
stepping: u8,
) -> Result<&'static str, ChipNameError> {
if family > 0x1E {
return Err(ChipNameError::InvalidFamily(family));
}
if stepping > 0xF {
return Err(ChipNameError::InvalidStepping(stepping));
}
chips::chip_name(vendor, family, model, stepping)
}
pub const fn core_name(
vendor: Vendor,
family: u8,
model: u8,
) -> Result<&'static str, CoreNameError> {
if family > 0x1E {
return Err(CoreNameError::InvalidFamily(family));
}
cores::core_name(vendor, family, model)
}
#[cfg(test)]
mod tests {
use super::*;
use strum::IntoEnumIterator;
#[test]
fn parse_vendor() {
assert!(matches!(
"InvalidIntel".parse::<Vendor>(),
Err(ParseVendorError::NoMatch(_))
));
assert_eq!(
"Invalid".parse::<Vendor>(),
Err(ParseVendorError::InvalidLength(7))
);
assert_eq!(
"InvalidIš³".parse::<Vendor>(),
Err(ParseVendorError::InvalidCharacters)
);
assert_eq!("GenuineIntel".parse::<Vendor>(), Ok(Vendor::Intel));
assert_eq!("AuthenticAMD".parse::<Vendor>(), Ok(Vendor::Amd));
}
#[test]
fn vendor_display() {
assert!(Vendor::iter().all(|it| !it.to_string().is_empty()));
assert_eq!(Vendor::Intel.to_string(), "Intel");
assert_eq!(Vendor::Amd.to_string(), "AMD");
}
#[test]
fn chip_names() {
assert_eq!(
chip_name(Vendor::Intel, 0xFF, 0x0, 0x0),
Err(ChipNameError::InvalidFamily(0xFF))
);
assert_eq!(
chip_name(Vendor::Intel, 0x06, 0x4E, 0xFF),
Err(ChipNameError::InvalidStepping(0xFF))
);
assert_eq!(chip_name(Vendor::Intel, 0x06, 0x4E, 0x0), Ok("Skylake-U/Y"));
assert_eq!(chip_name(Vendor::Amd, 0x15, 0x70, 0x0), Ok("Stoney Ridge"));
}
#[test]
fn core_names() {
assert_eq!(
core_name(Vendor::Intel, 0xFF, 0x0),
Err(CoreNameError::InvalidFamily(0xFF))
);
assert_eq!(core_name(Vendor::Intel, 0x06, 0x4E), Ok("Skylake"));
assert_eq!(core_name(Vendor::Amd, 0x15, 0x7F), Ok("Excavator"));
}
}