architecture 0.1.0

Commonly used constants for architectures, platforms, and related targets
Documentation
use core::fmt;
use core::str::FromStr;

/// CPU architecture.
///
/// Variants use Rust-canonical naming from target triples.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum Architecture {
    // x86 family
    X86,
    X86_64,

    // ARM 64-bit
    Aarch64,

    // ARM 32-bit
    Arm,
    Armv4t,
    Armv5te,
    Armv6,
    Armv6k,
    Armv7,
    Armv7a,
    Armv7k,
    Armv7r,
    Armv7s,
    Armeb,
    Armebv7r,

    // Thumb
    Thumbv4t,
    Thumbv5te,
    Thumbv6m,
    Thumbv7a,
    Thumbv7em,
    Thumbv7m,
    Thumbv7neon,
    Thumbv8mBase,
    Thumbv8mMain,

    // RISC-V
    Riscv32,
    Riscv64,

    // MIPS
    Mips,
    Mipsel,
    Mips32r6,
    Mips64,
    Mips64el,
    Mips64r6,

    // PowerPC
    Powerpc,
    Powerpc64,
    Powerpc64le,

    // Other
    S390x,
    Sparc,
    Sparc64,
    LoongArch64,
    Hexagon,
    M68k,
    Msp430,
    Avr,
    Nvptx64,
    Bpfeb,
    Bpfel,
    Xtensa,
    Csky,
    Wasm32,
    Wasm64,

    Unknown,
}

impl fmt::Display for Architecture {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Self::X86 => "x86",
            Self::X86_64 => "x86_64",
            Self::Aarch64 => "aarch64",
            Self::Arm => "arm",
            Self::Armv4t => "armv4t",
            Self::Armv5te => "armv5te",
            Self::Armv6 => "armv6",
            Self::Armv6k => "armv6k",
            Self::Armv7 => "armv7",
            Self::Armv7a => "armv7a",
            Self::Armv7k => "armv7k",
            Self::Armv7r => "armv7r",
            Self::Armv7s => "armv7s",
            Self::Armeb => "armeb",
            Self::Armebv7r => "armebv7r",
            Self::Thumbv4t => "thumbv4t",
            Self::Thumbv5te => "thumbv5te",
            Self::Thumbv6m => "thumbv6m",
            Self::Thumbv7a => "thumbv7a",
            Self::Thumbv7em => "thumbv7em",
            Self::Thumbv7m => "thumbv7m",
            Self::Thumbv7neon => "thumbv7neon",
            Self::Thumbv8mBase => "thumbv8m.base",
            Self::Thumbv8mMain => "thumbv8m.main",
            Self::Riscv32 => "riscv32",
            Self::Riscv64 => "riscv64",
            Self::Mips => "mips",
            Self::Mipsel => "mipsel",
            Self::Mips32r6 => "mips32r6",
            Self::Mips64 => "mips64",
            Self::Mips64el => "mips64el",
            Self::Mips64r6 => "mips64r6",
            Self::Powerpc => "powerpc",
            Self::Powerpc64 => "powerpc64",
            Self::Powerpc64le => "powerpc64le",
            Self::S390x => "s390x",
            Self::Sparc => "sparc",
            Self::Sparc64 => "sparc64",
            Self::LoongArch64 => "loongarch64",
            Self::Hexagon => "hexagon",
            Self::M68k => "m68k",
            Self::Msp430 => "msp430",
            Self::Avr => "avr",
            Self::Nvptx64 => "nvptx64",
            Self::Bpfeb => "bpfeb",
            Self::Bpfel => "bpfel",
            Self::Xtensa => "xtensa",
            Self::Csky => "csky",
            Self::Wasm32 => "wasm32",
            Self::Wasm64 => "wasm64",
            Self::Unknown => "unknown",
        };
        f.write_str(s)
    }
}

impl FromStr for Architecture {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "x86" => Ok(Self::X86),
            "x86_64" => Ok(Self::X86_64),
            "aarch64" => Ok(Self::Aarch64),
            "arm" => Ok(Self::Arm),
            "armv4t" => Ok(Self::Armv4t),
            "armv5te" => Ok(Self::Armv5te),
            "armv6" => Ok(Self::Armv6),
            "armv6k" => Ok(Self::Armv6k),
            "armv7" => Ok(Self::Armv7),
            "armv7a" => Ok(Self::Armv7a),
            "armv7k" => Ok(Self::Armv7k),
            "armv7r" => Ok(Self::Armv7r),
            "armv7s" => Ok(Self::Armv7s),
            "armeb" => Ok(Self::Armeb),
            "armebv7r" => Ok(Self::Armebv7r),
            "thumbv4t" => Ok(Self::Thumbv4t),
            "thumbv5te" => Ok(Self::Thumbv5te),
            "thumbv6m" => Ok(Self::Thumbv6m),
            "thumbv7a" => Ok(Self::Thumbv7a),
            "thumbv7em" => Ok(Self::Thumbv7em),
            "thumbv7m" => Ok(Self::Thumbv7m),
            "thumbv7neon" => Ok(Self::Thumbv7neon),
            "thumbv8m.base" => Ok(Self::Thumbv8mBase),
            "thumbv8m.main" => Ok(Self::Thumbv8mMain),
            "riscv32" => Ok(Self::Riscv32),
            "riscv64" => Ok(Self::Riscv64),
            "mips" => Ok(Self::Mips),
            "mipsel" => Ok(Self::Mipsel),
            "mips32r6" => Ok(Self::Mips32r6),
            "mips64" => Ok(Self::Mips64),
            "mips64el" => Ok(Self::Mips64el),
            "mips64r6" => Ok(Self::Mips64r6),
            "powerpc" => Ok(Self::Powerpc),
            "powerpc64" => Ok(Self::Powerpc64),
            "powerpc64le" => Ok(Self::Powerpc64le),
            "s390x" => Ok(Self::S390x),
            "sparc" => Ok(Self::Sparc),
            "sparc64" => Ok(Self::Sparc64),
            "loongarch64" => Ok(Self::LoongArch64),
            "hexagon" => Ok(Self::Hexagon),
            "m68k" => Ok(Self::M68k),
            "msp430" => Ok(Self::Msp430),
            "avr" => Ok(Self::Avr),
            "nvptx64" => Ok(Self::Nvptx64),
            "bpfeb" => Ok(Self::Bpfeb),
            "bpfel" => Ok(Self::Bpfel),
            "xtensa" => Ok(Self::Xtensa),
            "csky" => Ok(Self::Csky),
            "wasm32" => Ok(Self::Wasm32),
            "wasm64" => Ok(Self::Wasm64),
            "unknown" => Ok(Self::Unknown),

            // Common aliases
            "amd64" => Ok(Self::X86_64),
            "arm64" => Ok(Self::Aarch64),
            "i386" | "i586" | "i686" => Ok(Self::X86),
            "ppc" => Ok(Self::Powerpc),
            "ppc64" => Ok(Self::Powerpc64),
            "ppc64le" => Ok(Self::Powerpc64le),

            _ => Err(ParseError(())),
        }
    }
}

/// Error returned when parsing an unknown architecture or platform string.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseError(pub(crate) ());

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("unknown variant")
    }
}

impl std::error::Error for ParseError {}

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

    #[test]
    fn display_roundtrip() {
        let variants = [
            Architecture::X86,
            Architecture::X86_64,
            Architecture::Aarch64,
            Architecture::Arm,
            Architecture::Armv4t,
            Architecture::Armv5te,
            Architecture::Armv6,
            Architecture::Armv6k,
            Architecture::Armv7,
            Architecture::Armv7a,
            Architecture::Armv7k,
            Architecture::Armv7r,
            Architecture::Armv7s,
            Architecture::Armeb,
            Architecture::Armebv7r,
            Architecture::Thumbv4t,
            Architecture::Thumbv5te,
            Architecture::Thumbv6m,
            Architecture::Thumbv7a,
            Architecture::Thumbv7em,
            Architecture::Thumbv7m,
            Architecture::Thumbv7neon,
            Architecture::Thumbv8mBase,
            Architecture::Thumbv8mMain,
            Architecture::Riscv32,
            Architecture::Riscv64,
            Architecture::Mips,
            Architecture::Mipsel,
            Architecture::Mips32r6,
            Architecture::Mips64,
            Architecture::Mips64el,
            Architecture::Mips64r6,
            Architecture::Powerpc,
            Architecture::Powerpc64,
            Architecture::Powerpc64le,
            Architecture::S390x,
            Architecture::Sparc,
            Architecture::Sparc64,
            Architecture::LoongArch64,
            Architecture::Hexagon,
            Architecture::M68k,
            Architecture::Msp430,
            Architecture::Avr,
            Architecture::Nvptx64,
            Architecture::Bpfeb,
            Architecture::Bpfel,
            Architecture::Xtensa,
            Architecture::Csky,
            Architecture::Wasm32,
            Architecture::Wasm64,
            Architecture::Unknown,
        ];

        for variant in variants {
            let s = variant.to_string();
            let parsed: Architecture = s.parse().unwrap();
            assert_eq!(variant, parsed, "roundtrip failed for {s}");
        }
    }

    #[test]
    fn aliases() {
        assert_eq!(
            "amd64".parse::<Architecture>().unwrap(),
            Architecture::X86_64
        );
        assert_eq!(
            "arm64".parse::<Architecture>().unwrap(),
            Architecture::Aarch64
        );
        assert_eq!("i386".parse::<Architecture>().unwrap(), Architecture::X86);
        assert_eq!("i586".parse::<Architecture>().unwrap(), Architecture::X86);
        assert_eq!("i686".parse::<Architecture>().unwrap(), Architecture::X86);
        assert_eq!(
            "ppc".parse::<Architecture>().unwrap(),
            Architecture::Powerpc
        );
        assert_eq!(
            "ppc64".parse::<Architecture>().unwrap(),
            Architecture::Powerpc64
        );
        assert_eq!(
            "ppc64le".parse::<Architecture>().unwrap(),
            Architecture::Powerpc64le
        );
    }

    #[test]
    fn parse_error() {
        assert!("not_an_arch".parse::<Architecture>().is_err());
    }
}