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}