Skip to main content

sys_rs/
param.rs

1use core::fmt;
2use nix::errno::Errno;
3
4use crate::{
5    asm::Parser,
6    diag::{Error, Result},
7};
8
9/// The expected parameter type for a command argument.
10///
11/// This enum is used by the command registry to describe what kind of value
12/// a specific command parameter expects. It is primarily used to parse and
13/// validate user input when dispatching commands.
14pub enum Type {
15    /// A numeric address in hexadecimal form (e.g. `0x400123`).
16    Address,
17    /// A formatting specifier: one of `d`, `x`, `i` or `s`.
18    Format,
19    /// A numeric identifier (used for breakpoint ids, etc.).
20    Id,
21    /// A numeric size value.
22    Size,
23    /// A plain string value.
24    String,
25}
26
27impl fmt::Display for Type {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Type::Address => write!(f, "<address>"),
31            Type::Format => write!(f, "[d|x|i|s]"),
32            Type::Id => write!(f, "<id>"),
33            Type::Size => write!(f, "<size>"),
34            Type::String => write!(f, "<string>"),
35        }
36    }
37}
38
39#[derive(Clone)]
40/// How to format bytes when examining memory.
41///
42/// - `Decimal` prints bytes as decimal numbers.
43/// - `Hexadecimal` prints bytes in hexadecimal.
44/// - `Instruction` interprets the buffer as machine code and disassembles it.
45/// - `String` treats the buffer as UTF-8 and prints the string.
46pub enum Format {
47    /// Print each byte as decimal.
48    Decimal,
49    /// Print each byte as hexadecimal.
50    Hexadecimal,
51    /// Disassemble the buffer into instructions and print them.
52    Instruction,
53    /// Print the buffer as a UTF-8 string.
54    String,
55}
56
57impl Format {
58    /// Format and print `buf` according to this `Format`.
59    ///
60    /// # Arguments
61    ///
62    /// * `buf` - The byte buffer to format.
63    /// * `addr` - Base address to use when disassembling instructions (only
64    ///   used for `Format::Instruction`).
65    ///
66    /// # Errors
67    ///
68    /// Returns an error if instruction disassembly is requested and the
69    /// underlying parser fails.
70    pub fn bytes(&self, buf: &[u8], addr: u64) -> Result<()> {
71        match self {
72            Format::Decimal | Format::Hexadecimal => {
73                for byte in buf {
74                    match self {
75                        Format::Decimal => print!("{byte} "),
76                        Format::Hexadecimal => print!("{byte:x} "),
77                        _ => unreachable!(),
78                    }
79                }
80                println!();
81            }
82            Format::Instruction => {
83                let parser = Parser::new()?;
84                let instructions = parser.get_all_instructions_from(buf, addr)?;
85                for instruction in instructions {
86                    println!("{instruction}");
87                }
88            }
89            Format::String => println!("{}", String::from_utf8_lossy(buf)),
90        }
91
92        Ok(())
93    }
94}
95
96impl TryFrom<char> for Format {
97    type Error = Error;
98
99    fn try_from(c: char) -> Result<Self> {
100        match c {
101            'd' => Ok(Format::Decimal),
102            'x' => Ok(Format::Hexadecimal),
103            'i' => Ok(Format::Instruction),
104            's' => Ok(Format::String),
105            _ => Err(Error::from(Errno::EINVAL)),
106        }
107    }
108}
109
110impl fmt::Display for Format {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        match self {
113            Format::Decimal => write!(f, "d"),
114            Format::Hexadecimal => write!(f, "x"),
115            Format::Instruction => write!(f, "i"),
116            Format::String => write!(f, "s"),
117        }
118    }
119}
120
121/// A parsed command argument value.
122///
123/// Instances of `Value` represent concrete arguments that were parsed from
124/// user input according to the expected `Type` for a command parameter.
125pub enum Value<'a> {
126    /// Parsed address value.
127    Address(u64),
128    /// A format specifier value.
129    Format(Format),
130    /// Numeric identifier.
131    Id(u64),
132    /// Numeric size.
133    Size(u64),
134    /// A borrowed string slice.
135    String(&'a str),
136}
137
138impl<'a> Value<'a> {
139    /// Parse a parameter string into a typed `Value` according to `param_type`.
140    ///
141    /// # Arguments
142    ///
143    /// * `param_type` - The expected `Type` describing how to parse `param`.
144    /// * `param` - The raw string slice containing the parameter to parse.
145    ///
146    /// # Errors
147    ///
148    /// Returns an `Err` if the parameter cannot be parsed according to
149    /// `param_type` (for example an invalid hex address or non-numeric id).
150    pub fn new(param_type: &Type, param: &'a str) -> Result<Self> {
151        match param_type {
152            Type::Address => Self::address(param),
153            Type::Format => Self::format(param),
154            Type::Id => Self::id(param),
155            Type::Size => Self::size(param),
156            Type::String => Ok(Self::string(param)),
157        }
158    }
159
160    fn address(param: &str) -> Result<Self> {
161        param
162            .strip_prefix("0x")
163            .and_then(|s| u64::from_str_radix(s, 16).ok())
164            .map(Value::Address)
165            .ok_or_else(|| Error::from(Errno::EINVAL))
166    }
167
168    fn format(param: &str) -> Result<Self> {
169        let format = Format::try_from(
170            param.chars().next().ok_or(Error::from(Errno::EINVAL))?,
171        )?;
172        Ok(Value::Format(format))
173    }
174
175    fn id(param: &str) -> Result<Self> {
176        param
177            .parse::<u64>()
178            .map(Value::Id)
179            .map_err(|_| Error::from(Errno::EINVAL))
180    }
181
182    fn size(param: &str) -> Result<Self> {
183        param
184            .parse::<u64>()
185            .map(Value::Size)
186            .map_err(|_| Error::from(Errno::EINVAL))
187    }
188
189    fn string(param: &'a str) -> Self {
190        Value::String(param)
191    }
192}
193
194impl fmt::Display for Value<'_> {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        match self {
197            Value::Address(addr) => write!(f, "{addr:#x}"),
198            Value::Format(fmt) => write!(f, "{fmt}"),
199            Value::Id(id) => write!(f, "{id}"),
200            Value::Size(size) => write!(f, "{size}"),
201            Value::String(s) => write!(f, "{s}"),
202        }
203    }
204}
205
206/// Helpers to extend a parsed `Value` path with additional string arguments.
207///
208/// This trait is used by the command dispatch code to build a `Vec<Value>`
209/// representing the command path plus the user provided arguments. For
210/// example, it allows turning `[]` into `[Value::String("help")]` or to
211/// append further tokens.
212pub trait Extend<'a> {
213    /// Extend the current slice of parsed `Value`s with additional string
214    /// arguments and return a new `Vec<Value>`.
215    ///
216    /// This is used by the command dispatch code to create a concrete
217    /// argument vector that contains the existing parsed values followed by
218    /// the provided `first` argument and any `rest` arguments. The returned
219    /// vector contains cloned/copy variants of the original `Value`s.
220    ///
221    /// # Arguments
222    ///
223    /// * `self` - The slice of already-parsed `Value` arguments to extend.
224    /// * `first` - The first additional argument to append (becomes a
225    ///   `Value::String`).
226    /// * `rest` - Remaining additional arguments to append (each becomes a
227    ///   `Value::String`).
228    ///
229    /// # Returns
230    ///
231    /// A newly allocated `Vec<Value<'a>>` containing the original values (as
232    /// copies) followed by `first` and the elements of `rest` converted to
233    /// `Value::String`.
234    fn extend(&self, first: &'a str, rest: &'a [&'a str]) -> Vec<Value<'a>>;
235}
236
237impl<'a> Extend<'a> for [Value<'a>] {
238    fn extend(&self, first: &'a str, rest: &'a [&'a str]) -> Vec<Value<'a>> {
239        self.iter()
240            .map(|v| match v {
241                Value::Address(addr) => Value::Address(*addr),
242                Value::Format(fmt) => Value::Format(fmt.clone()),
243                Value::Id(id) => Value::Id(*id),
244                Value::Size(size) => Value::Size(*size),
245                Value::String(s) => Value::String(s),
246            })
247            .chain(std::iter::once(Value::String(first)))
248            .chain(rest.iter().map(|&s| Value::String(s)))
249            .collect()
250    }
251}
252
253/// Join a slice of `Value` into a single `String` using `sep` as separator.
254///
255/// This is a convenience used by handlers when composing user-visible
256/// messages from an array of previously parsed `Value` arguments.
257pub trait Join {
258    /// Join the display representation of `self` using `sep`.
259    ///
260    /// # Arguments
261    ///
262    /// * `self` - The slice of `Value` items to join.
263    /// * `sep` - Separator string inserted between each item's display
264    ///   representation.
265    ///
266    /// # Returns
267    ///
268    /// A `String` containing each element's `Display` output separated by
269    /// `sep`.
270    fn join(&self, sep: &str) -> String;
271}
272
273impl Join for [Value<'_>] {
274    fn join(&self, sep: &str) -> String {
275        self.iter()
276            .map(ToString::to_string)
277            .collect::<Vec<_>>()
278            .join(sep)
279    }
280}
281
282#[cfg(test)]
283mod tests {
284    use super::*;
285
286    #[test]
287    fn test_parse_address_ok() {
288        let v = Value::new(&Type::Address, "0x1000").expect("parse address");
289        match v {
290            Value::Address(a) => assert_eq!(a, 0x1000),
291            _ => panic!("expected Address variant"),
292        }
293    }
294
295    #[test]
296    fn test_parse_id_err() {
297        let r = Value::new(&Type::Id, "not-a-number");
298        assert!(r.is_err());
299    }
300
301    #[test]
302    fn test_extend_and_join() {
303        let base: &[Value] = &[];
304        let out = base.extend("help", &["me", "now"]);
305        assert_eq!(out.len(), 3);
306        assert_eq!(out.join(" "), "help me now");
307    }
308
309    #[test]
310    fn test_format_try_from_ok() {
311        let f = Format::try_from('d').expect("format parse");
312        assert!(matches!(f, Format::Decimal));
313    }
314}