libcpuname 0.1.3

Identify CPU vendors, chips, and cores across multiple architectures
Documentation
use err::{CoreNameError, TryFromIntError};

mod cores;
/// Errors that can be produced by functions in this module.
pub mod err;

#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[cfg_attr(test, derive(strum::EnumIter))]
#[non_exhaustive]
#[repr(u8)]
/// Known hardware implementers of the ARM instruction set family.
///
/// Each value in this enum corresponds to a known ARM ISA implementer with a registered ID.
/// All IDs are assigned by [ARM Holdings](https://www.arm.com/) and any unassigned values are reserved.
/// The implementer field in the `MIDR` register is 8-bit unsigned and this enum mirrors that representation.
/// The [`TryFrom`] trait can be used to convert any integer into one of these enum values.
///
/// # Example
///
/// ```rust
/// # fn main() -> Result<(), libcpuname::arm::err::TryFromIntError> {
/// let implementer = libcpuname::arm::Implementer::try_from(0x41)?;
/// println!("implementer='{implementer}'");
/// # Ok(())
/// # }
/// ```
pub enum Implementer {
    /// Corresponds to the British company [Arm Holdings](https://www.arm.com/).
    Arm = 0x41,
    /// Corresponds to the American company [Broadcom](https://www.broadcom.com/).
    Broadcom = 0x42,
    /// Corresponds to the now-defunct American company Cavium.
    Cavium = 0x43,
    /// Corresponds to the now-defunct American company Digital Equipment Corporation.
    Dec = 0x44,
    /// Corresponds to the Japanese company [Fujitsu](https://www.fujitsu.com/).
    Fujitsu = 0x46,
    /// Corresponds to the Chinese company [HiSilicon](http://www.hisilicon.com/).
    Hisilicon = 0x48,
    /// Corresponds to the German company [Infineon Technologies](https://www.infineon.com/).
    Infineon = 0x49,
    /// Corresponds to the now-defunct American companies Motorola and Freescale Semiconductor.
    /// Whether this ID is used by the active American companies [Motorola Mobility](https://www.motorola.com/) and [Motorola Solutions](https://www.motorolasolutions.com/) is unknown.
    MotorolaOrFreescale = 0x4D,
    /// Corresponds to the American company [Nvidia](https://www.nvidia.com/).
    Nvidia = 0x4E,
    /// Corresponds to the now-defunct American company Applied Micro Circuits.
    Apm = 0x50,
    /// Corresponds to the American company [Qualcomm](https://www.qualcomm.com/).
    Qualcomm = 0x51,
    /// Corresponds to the Korean company [SAMSUNG](https://www.samsung.com/).
    Samsung = 0x53,
    /// Corresponds to the American company [Texas Instruments](https://www.ti.com/).
    TexasInstruments = 0x54,
    /// Corresponds to the American company [Marvell Technology](https://www.marvell.com/).
    Marvell = 0x56,
    /// Corresponds to the American company [Apple](https://www.apple.com/).
    Apple = 0x61,
    /// Corresponds to the Taiwanese company [Faraday Technology](http://www.faraday-tech.com/).
    Faraday = 0x66,
    /// Corresponds to the now-defunct Chinese joint venture company HXT Semiconductor Technologies.
    Hxt = 0x68,
    /// Corresponds to the American company [Intel](https://www.intel.com/).
    Intel = 0x69,
    /// Corresponds to the American company [Microsoft](https://www.microsoft.com/).
    Microsoft = 0x6D,
    /// Corresponds to the Chinese company [Phytium Technology Co](https://www.phytium.com.cn/).
    Phytium = 0x70,
    /// Corresponds to the American company [Ampere Computing](https://amperecomputing.com/).
    Ampere = 0xC0,
}

impl core::fmt::Display for Implementer {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let s = match self {
            Self::Arm => "ARM",
            Self::Broadcom => "Broadcom",
            Self::Cavium => "Cavium",
            Self::Dec => "DEC",
            Self::Fujitsu => "Fujitsu",
            Self::Hisilicon => "HiSilicon",
            Self::Infineon => "Infineon",
            Self::MotorolaOrFreescale => "Motorola/Freescale",
            Self::Nvidia => "NVIDIA",
            Self::Apm => "APM",
            Self::Qualcomm => "Qualcomm",
            Self::Samsung => "Samsung",
            Self::TexasInstruments => "Texas Instruments",
            Self::Marvell => "Marvell",
            Self::Apple => "Apple",
            Self::Faraday => "Faraday",
            Self::Hxt => "HXT",
            Self::Intel => "Intel",
            Self::Microsoft => "Microsoft",
            Self::Phytium => "Phytium",
            Self::Ampere => "Ampere",
        };

        write!(f, "{s}")
    }
}

macro_rules! try_from_impl_for_implementer {
    ($t:tt) => {
        impl TryFrom<$t> for Implementer {
            type Error = TryFromIntError;

            fn try_from(v: $t) -> Result<Self, Self::Error> {
                #[allow(irrefutable_let_patterns)]
                let v = if let Ok(vu8) = u8::try_from(v) {
                    vu8
                } else {
                    return Err(TryFromIntError);
                };

                match v {
                    v if v == Implementer::Arm as u8 => Ok(Implementer::Arm),
                    v if v == Implementer::Broadcom as u8 => Ok(Implementer::Broadcom),
                    v if v == Implementer::Cavium as u8 => Ok(Implementer::Cavium),
                    v if v == Implementer::Dec as u8 => Ok(Implementer::Dec),
                    v if v == Implementer::Fujitsu as u8 => Ok(Implementer::Fujitsu),
                    v if v == Implementer::Hisilicon as u8 => Ok(Implementer::Hisilicon),
                    v if v == Implementer::Infineon as u8 => Ok(Implementer::Infineon),
                    v if v == Implementer::MotorolaOrFreescale as u8 => {
                        Ok(Implementer::MotorolaOrFreescale)
                    }
                    v if v == Implementer::Nvidia as u8 => Ok(Implementer::Nvidia),
                    v if v == Implementer::Apm as u8 => Ok(Implementer::Apm),
                    v if v == Implementer::Qualcomm as u8 => Ok(Implementer::Qualcomm),
                    v if v == Implementer::Samsung as u8 => Ok(Implementer::Samsung),
                    v if v == Implementer::TexasInstruments as u8 => {
                        Ok(Implementer::TexasInstruments)
                    }
                    v if v == Implementer::Marvell as u8 => Ok(Implementer::Marvell),
                    v if v == Implementer::Apple as u8 => Ok(Implementer::Apple),
                    v if v == Implementer::Faraday as u8 => Ok(Implementer::Faraday),
                    v if v == Implementer::Hxt as u8 => Ok(Implementer::Hxt),
                    v if v == Implementer::Intel as u8 => Ok(Implementer::Intel),
                    v if v == Implementer::Microsoft as u8 => Ok(Implementer::Microsoft),
                    v if v == Implementer::Phytium as u8 => Ok(Implementer::Phytium),
                    v if v == Implementer::Ampere as u8 => Ok(Implementer::Ampere),
                    _ => Err(TryFromIntError),
                }
            }
        }
    };
}

try_from_impl_for_implementer!(u8);
try_from_impl_for_implementer!(u16);
try_from_impl_for_implementer!(u32);
try_from_impl_for_implementer!(u64);
try_from_impl_for_implementer!(u128);
try_from_impl_for_implementer!(usize);

try_from_impl_for_implementer!(i8);
try_from_impl_for_implementer!(i16);
try_from_impl_for_implementer!(i32);
try_from_impl_for_implementer!(i64);
try_from_impl_for_implementer!(i128);
try_from_impl_for_implementer!(isize);

macro_rules! from_implementer_impl_for_int {
    ($t:tt) => {
        impl From<Implementer> for $t {
            fn from(value: Implementer) -> Self {
                value as $t
            }
        }
    };
}

from_implementer_impl_for_int!(u8);
from_implementer_impl_for_int!(u16);
from_implementer_impl_for_int!(u32);
from_implementer_impl_for_int!(u64);
from_implementer_impl_for_int!(u128);
from_implementer_impl_for_int!(usize);

from_implementer_impl_for_int!(i16);
from_implementer_impl_for_int!(i32);
from_implementer_impl_for_int!(i64);
from_implementer_impl_for_int!(i128);
from_implementer_impl_for_int!(isize);

/// Find the codenames of a CPU's cores based on values derived from the contents of the `MIDR` register.
///
/// This function will return a string with a static lifetime that represents the core microarchitecture(s) that correspond to the input `MIDR`-derived values.
/// Each parameter is dependent on the one before it - for example, a `variant` value of `0x4F` will return a different result if the `implementer` or `part` values change.
/// If the input values correspond to multiple cores (such as Falkor-V1 and Kryo), the returned string will contain all known names that correspond to those values, separated by the delimiter ` / ` (e.g. `Falkor-V1 / Kryo`).
/// Additionally, if the input values correspond to multiple variants of one core (such as Exynos-M1 and Exynos-M2), the returned string will separate the variant markers with the delimiter `/` (e.g. `Exynos-M1/M2`).
///
/// This function is marked as `const` and may be evaluated at compile-time if the arguments are static.
///
/// # Errors
///
/// An error will be returned if the input could not be resolved to a known core name.
///
/// # Example
///
/// ```rust
/// # fn main() -> Result<(), libcpuname::arm::err::Error> {
/// let implementer = libcpuname::arm::Implementer::try_from(0x41)?;
/// let core = libcpuname::arm::core_name(implementer, 0xD03, 0x0)?;
/// println!("implementer='{implementer}', core='{core}'");
/// # Ok(())
/// # }
/// ```
pub const fn core_name(
    implementer: Implementer,
    part: u16,
    variant: u8,
) -> Result<&'static str, CoreNameError> {
    if part > 0xFFF {
        return Err(CoreNameError::InvalidPart(part));
    }

    if variant > 0xF {
        return Err(CoreNameError::InvalidVariant(variant));
    }

    cores::core_name(implementer, part, variant)
}

#[cfg(test)]
mod tests {
    use super::*;
    use strum::IntoEnumIterator;

    #[test]
    fn implementer_typeconv() {
        Implementer::iter().for_each(|it| {
            let converted = u32::from(it).try_into();
            assert!(converted.is_ok_and(|c: Implementer| c == it));
        });

        assert_eq!(Implementer::try_from(0xFF), Err(TryFromIntError));
        assert_eq!(Implementer::try_from(0xFFFF), Err(TryFromIntError));
    }

    #[test]
    fn implementer_display() {
        assert!(Implementer::iter().all(|it| !it.to_string().is_empty()));
        assert_eq!(Implementer::Arm.to_string(), "ARM");
    }

    #[test]
    fn core_names() {
        assert_eq!(
            core_name(Implementer::Arm, 0xFFFF, 0x0),
            Err(CoreNameError::InvalidPart(0xFFFF))
        );
        assert_eq!(
            core_name(Implementer::Arm, 0x001, 0xFF),
            Err(CoreNameError::InvalidVariant(0xFF))
        );
        assert_eq!(core_name(Implementer::Arm, 0xD07, 0x0), Ok("Cortex-A57"));
        assert_eq!(core_name(Implementer::Apple, 0x006, 0x0), Ok("Hurricane"));
    }
}