use clap::{Parser, Subcommand, ValueEnum};
use std::error::Error;
use std::fmt;
use std::str::FromStr;
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub struct VirtualAddress(u64);
impl fmt::Display for VirtualAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{:016x}", self.0)
}
}
impl VirtualAddress {
const PREFIX: &'static str = "0x";
}
#[derive(Copy, Clone, Debug, derive_more::Display, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub enum VirtualAddressError {
#[display("The virtual address must begin with the prefix 0x.")]
MissingPrefix,
#[display("The virtual address could not be parsed as number as `u64`.")]
ParseIntError,
}
impl Error for VirtualAddressError {}
impl From<u64> for VirtualAddress {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<VirtualAddress> for u64 {
fn from(value: VirtualAddress) -> Self {
value.0
}
}
impl From<VirtualAddress> for u32 {
fn from(value: VirtualAddress) -> Self {
(value.0 & 0xffffffff) as Self
}
}
impl FromStr for VirtualAddress {
type Err = VirtualAddressError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim().to_lowercase().replace('_', "");
if !s.starts_with(Self::PREFIX) {
return Err(VirtualAddressError::MissingPrefix);
}
let s_without_prefix = &s.as_str()[Self::PREFIX.len()..];
u64::from_str_radix(s_without_prefix, 16)
.map(Self)
.map_err(|e| {
eprintln!("{e}");
VirtualAddressError::ParseIntError
})
}
}
#[derive(Parser)]
#[command(author, version, about)]
pub struct CliArgs {
#[arg()]
pub virtual_address: VirtualAddress,
#[command(subcommand)]
pub architecture: Architecture,
#[arg(long, value_enum)]
pub color: Option<ColorOption>,
}
#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq, Ord, Eq, Hash, ValueEnum)]
pub enum ColorOption {
Never,
#[default]
Auto,
Always,
}
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Subcommand)]
pub enum Architecture {
X86 {
#[arg(long, default_value = "false")]
pae: bool,
},
#[command(id = "x86_64")]
X86_64 {
#[arg(short = '5', long, default_value = "false")]
five_level: bool,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_virtual_addr_from_str() {
assert_eq!(VirtualAddress::from_str("0x123"), Ok(0x123.into()));
assert_eq!(
VirtualAddress::from_str("0xdead_beef"),
Ok(0xdead_beef.into())
);
assert_eq!(
VirtualAddress::from_str(" 0xdEAd_bEEF "),
Ok(0xdead_beef.into())
);
}
#[test]
fn test_virtual_addr_64_to_32_bit() {
let v_addr = VirtualAddress::from_str("0xdead_beef_1337_1337");
assert_eq!(v_addr, Ok(0xdead_beef_1337_1337.into()));
let v_addr = v_addr.unwrap();
assert_eq!(u32::from(v_addr), 0x1337_1337);
}
}