use std::str::FromStr;
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum SyscallAbi {
Linux,
Hurd,
FreeBSD,
Uefi,
Other(String),
}
impl SyscallAbi {
pub fn as_str(&self) -> &str {
match self {
Self::Linux => "linux",
Self::Hurd => "gnu",
Self::FreeBSD => "kfreebsd",
Self::Uefi => "uefi",
Self::Other(v) => v.as_str(),
}
}
}
impl std::fmt::Display for SyscallAbi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum SyscallAbiParseError {
Empty,
}
crate::errors::error_enum!(SyscallAbiParseError);
impl FromStr for SyscallAbi {
type Err = SyscallAbiParseError;
fn from_str(abi: &str) -> Result<Self, SyscallAbiParseError> {
Ok(match abi {
"" => return Err(SyscallAbiParseError::Empty),
"linux" => Self::Linux,
"gnu" => Self::Hurd,
"kfreebsd" => Self::FreeBSD,
"uefi" => Self::Uefi,
_ => Self::Other(abi.to_owned()),
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum InstructionSetParseError {
Empty,
}
crate::errors::error_enum!(InstructionSetParseError);
macro_rules! instruction_set_table_as_str {
( $( ( $id:ident, $name:expr, $isa:path ) ),* ) => {
impl InstructionSet {
pub fn as_str(&self) -> &str {
match self {
$( $isa => $name, )*
Self::Other(v) => v.as_str(),
}
}
}
};
}
impl std::fmt::Display for InstructionSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
macro_rules! instruction_set_table_from_str {
( $( ( $id:ident, $name:expr, $isa:path ) ),* ) => {
impl FromStr for InstructionSet {
type Err = InstructionSetParseError;
fn from_str(isa: &str) -> Result<Self, InstructionSetParseError> {
Ok(match isa {
"" => return Err(InstructionSetParseError::Empty),
$( $name => $isa, )*
_ => InstructionSet::Other(isa.to_owned()),
})
}
}
};
}
#[allow(unused_macros)]
macro_rules! instruction_set_table_tests {
( $( ( $id:ident, $name:expr, $isa:path ) ),* ) => {
$(
#[test]
fn $id() {
let isa: InstructionSet = $name.parse().unwrap();
assert_eq!($name, isa.as_str());
}
)*
};
}
macro_rules! instruction_set_table {
( $( ( $id:ident, $name:expr, $isa:path ) ),* ) => {
instruction_set_table_from_str!($( ($id, $name, $isa) ),*);
instruction_set_table_as_str!($( ($id, $name, $isa) ),*);
#[cfg(test)]
mod arch_tests {
use super::*;
instruction_set_table_tests!($( ($id, $name, $isa) ),*);
}
};
}
instruction_set_table!(
(aarch64, "aarch64", InstructionSet::Aarch64),
(alpha, "alpha", InstructionSet::Alpha),
(arc, "arc", InstructionSet::Arc),
(arm, "arm", InstructionSet::Arm),
(hppa, "hppa", InstructionSet::Hppa),
(i386, "i386", InstructionSet::I386),
(ia64, "ia64", InstructionSet::Ia64),
(loongarch64, "loongarch64", InstructionSet::Loongarch64),
(m68k, "m68k", InstructionSet::M68k),
(mips, "mips", InstructionSet::Mips),
(mipsel, "mipsel", InstructionSet::Mipsel),
(mips64, "mips64", InstructionSet::Mips64),
(mips64el, "mips64el", InstructionSet::Mips64el),
(mipsisa32r6, "mipsisa32r6", InstructionSet::MipsIsa32r6),
(
mipsisa32r6el,
"mipsisa32r6el",
InstructionSet::MipsIsa32r6el
),
(mipsisa64r6, "mipsisa64r6", InstructionSet::MipsIsa64r6),
(
mipsisa64r6el,
"mipsisa64r6el",
InstructionSet::MipsIsa64r6el
),
(powerpc, "powerpc", InstructionSet::PowerPc),
(powerpc64, "powerpc64", InstructionSet::PowerPc64),
(powerpc64el, "powerpc64el", InstructionSet::PowerPc64le),
(riscv64, "riscv64", InstructionSet::RiscV64),
(s390, "s390", InstructionSet::S390),
(s390x, "s390x", InstructionSet::S390X),
(sh4, "sh4", InstructionSet::Sh4),
(sparc, "sparc", InstructionSet::Sparc),
(sparc64, "sparc64", InstructionSet::Sparc64),
(x86_64, "x86_64", InstructionSet::X86_64)
);
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum InstructionSet {
Aarch64,
Alpha,
Arc,
Arm,
Hppa,
I386,
Ia64,
Loongarch64,
M68k,
Mips,
Mipsel,
Mips64,
Mips64el,
MipsIsa32r6,
MipsIsa32r6el,
MipsIsa64r6,
MipsIsa64r6el,
PowerPc,
PowerPc64,
PowerPc64le,
RiscV64,
S390,
S390X,
Sh4,
Sparc,
Sparc64,
X86_64,
Other(String),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Tuple {
pub instruction_set: InstructionSet,
pub syscall_abi: SyscallAbi,
pub userland: String,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TupleParseError {
Empty,
Malformed,
BadInstructionSet(InstructionSetParseError),
BadSyscallAbi(SyscallAbiParseError),
}
crate::errors::error_enum!(TupleParseError);
impl From<InstructionSetParseError> for TupleParseError {
fn from(e: InstructionSetParseError) -> Self {
Self::BadInstructionSet(e)
}
}
impl From<SyscallAbiParseError> for TupleParseError {
fn from(e: SyscallAbiParseError) -> Self {
Self::BadSyscallAbi(e)
}
}
impl std::fmt::Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let isa = self.instruction_set.as_str();
let abi = self.syscall_abi.as_str();
let userland = self.userland.as_str();
write!(
f,
"{}",
if abi == userland {
format!("{isa}-{abi}")
} else {
format!("{isa}-{abi}-{userland}")
}
)
}
}
impl FromStr for Tuple {
type Err = TupleParseError;
fn from_str(tuple: &str) -> Result<Self, TupleParseError> {
if tuple.is_empty() {
return Err(TupleParseError::Empty);
}
let chunks: Vec<&str> = tuple.split("-").collect();
Ok(match chunks[..] {
[isa, abi, userland] => Tuple {
instruction_set: isa.parse()?,
syscall_abi: abi.parse()?,
userland: userland.to_owned(),
},
[isa, abi] => Tuple {
instruction_set: isa.parse()?,
syscall_abi: abi.parse()?,
userland: abi.to_owned(),
},
_ => return Err(TupleParseError::Malformed),
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn check_syscall_abi_to_string() {
assert_eq!("linux", SyscallAbi::Linux.as_str());
assert_eq!("kfreebsd", SyscallAbi::FreeBSD.as_str());
assert_eq!("gnu", SyscallAbi::Hurd.as_str());
assert_eq!("uefi", SyscallAbi::Uefi.as_str());
assert_eq!(
"somethingelse",
SyscallAbi::Other("somethingelse".to_owned()).as_str()
);
}
#[test]
fn check_syscall_abi_from_string() {
assert_eq!(SyscallAbi::Linux, "linux".parse().unwrap());
assert_eq!(SyscallAbi::FreeBSD, "kfreebsd".parse().unwrap());
assert_eq!(SyscallAbi::Hurd, "gnu".parse().unwrap());
assert_eq!(SyscallAbi::Uefi, "uefi".parse().unwrap());
assert_eq!(
SyscallAbi::Other("somethingelse".to_owned()),
"somethingelse".parse().unwrap(),
);
}
#[test]
fn check_tuple_parse_simple() {
let tuple: Tuple = "x86_64-linux-gnu".parse().unwrap();
assert_eq!(
Tuple {
instruction_set: InstructionSet::X86_64,
syscall_abi: SyscallAbi::Linux,
userland: "gnu".to_owned(),
},
tuple
);
}
#[test]
fn check_tuple_parse_short() {
let tuple: Tuple = "x86_64-uefi".parse().unwrap();
assert_eq!(
Tuple {
instruction_set: InstructionSet::X86_64,
syscall_abi: SyscallAbi::Uefi,
userland: "uefi".to_owned(),
},
tuple
);
}
#[test]
fn check_tuple_parse_invalid() {
assert!("".parse::<Tuple>().is_err());
assert!("aarch64-linux-gnu-somethingelse".parse::<Tuple>().is_err());
assert!("sparc".parse::<Tuple>().is_err());
}
}