use core::fmt;
use nix::errno::Errno;
use crate::{
asm::Parser,
diag::{Error, Result},
};
pub enum Type {
Address,
Format,
Id,
Size,
String,
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Address => write!(f, "<address>"),
Type::Format => write!(f, "[d|x|i|s]"),
Type::Id => write!(f, "<id>"),
Type::Size => write!(f, "<size>"),
Type::String => write!(f, "<string>"),
}
}
}
#[derive(Clone)]
pub enum Format {
Decimal,
Hexadecimal,
Instruction,
String,
}
impl Format {
pub fn bytes(&self, buf: &[u8], addr: u64) -> Result<()> {
match self {
Format::Decimal | Format::Hexadecimal => {
for byte in buf {
match self {
Format::Decimal => print!("{byte} "),
Format::Hexadecimal => print!("{byte:x} "),
_ => unreachable!(),
}
}
println!();
}
Format::Instruction => {
let parser = Parser::new()?;
let instructions = parser.get_all_instructions_from(buf, addr)?;
for instruction in instructions {
println!("{instruction}");
}
}
Format::String => println!("{}", String::from_utf8_lossy(buf)),
}
Ok(())
}
}
impl TryFrom<char> for Format {
type Error = Error;
fn try_from(c: char) -> Result<Self> {
match c {
'd' => Ok(Format::Decimal),
'x' => Ok(Format::Hexadecimal),
'i' => Ok(Format::Instruction),
's' => Ok(Format::String),
_ => Err(Error::from(Errno::EINVAL)),
}
}
}
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Format::Decimal => write!(f, "d"),
Format::Hexadecimal => write!(f, "x"),
Format::Instruction => write!(f, "i"),
Format::String => write!(f, "s"),
}
}
}
pub enum Value<'a> {
Address(u64),
Format(Format),
Id(u64),
Size(u64),
String(&'a str),
}
impl<'a> Value<'a> {
pub fn new(param_type: &Type, param: &'a str) -> Result<Self> {
match param_type {
Type::Address => Self::address(param),
Type::Format => Self::format(param),
Type::Id => Self::id(param),
Type::Size => Self::size(param),
Type::String => Ok(Self::string(param)),
}
}
fn address(param: &str) -> Result<Self> {
param
.strip_prefix("0x")
.and_then(|s| u64::from_str_radix(s, 16).ok())
.map(Value::Address)
.ok_or_else(|| Error::from(Errno::EINVAL))
}
fn format(param: &str) -> Result<Self> {
let format = Format::try_from(
param.chars().next().ok_or(Error::from(Errno::EINVAL))?,
)?;
Ok(Value::Format(format))
}
fn id(param: &str) -> Result<Self> {
param
.parse::<u64>()
.map(Value::Id)
.map_err(|_| Error::from(Errno::EINVAL))
}
fn size(param: &str) -> Result<Self> {
param
.parse::<u64>()
.map(Value::Size)
.map_err(|_| Error::from(Errno::EINVAL))
}
fn string(param: &'a str) -> Self {
Value::String(param)
}
}
impl fmt::Display for Value<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Address(addr) => write!(f, "{addr:#x}"),
Value::Format(fmt) => write!(f, "{fmt}"),
Value::Id(id) => write!(f, "{id}"),
Value::Size(size) => write!(f, "{size}"),
Value::String(s) => write!(f, "{s}"),
}
}
}
pub trait Extend<'a> {
fn extend(&self, first: &'a str, rest: &'a [&'a str]) -> Vec<Value<'a>>;
}
impl<'a> Extend<'a> for [Value<'a>] {
fn extend(&self, first: &'a str, rest: &'a [&'a str]) -> Vec<Value<'a>> {
self.iter()
.map(|v| match v {
Value::Address(addr) => Value::Address(*addr),
Value::Format(fmt) => Value::Format(fmt.clone()),
Value::Id(id) => Value::Id(*id),
Value::Size(size) => Value::Size(*size),
Value::String(s) => Value::String(s),
})
.chain(std::iter::once(Value::String(first)))
.chain(rest.iter().map(|&s| Value::String(s)))
.collect()
}
}
pub trait Join {
fn join(&self, sep: &str) -> String;
}
impl Join for [Value<'_>] {
fn join(&self, sep: &str) -> String {
self.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(sep)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_address_ok() {
let v = Value::new(&Type::Address, "0x1000").expect("parse address");
match v {
Value::Address(a) => assert_eq!(a, 0x1000),
_ => panic!("expected Address variant"),
}
}
#[test]
fn test_parse_id_err() {
let r = Value::new(&Type::Id, "not-a-number");
assert!(r.is_err());
}
#[test]
fn test_extend_and_join() {
let base: &[Value] = &[];
let out = base.extend("help", &["me", "now"]);
assert_eq!(out.len(), 3);
assert_eq!(out.join(" "), "help me now");
}
#[test]
fn test_format_try_from_ok() {
let f = Format::try_from('d').expect("format parse");
assert!(matches!(f, Format::Decimal));
}
}