qemu_command_builder/args/
mon.rs1use crate::parsers::ARG_MON;
2use std::fmt::{Display, Formatter};
3use std::str::FromStr;
4
5use bon::Builder;
6use proptest_derive::Arbitrary;
7
8use crate::common::OnOff;
9use crate::parsers::DELIM_COMMA;
10use crate::qao;
11use crate::shell_string::{ShellString, ShellStringError};
12use crate::to_command::{ToArg, ToCommand};
13
14const KEY_CHARDEV: &str = "chardev=";
15const KEY_ID: &str = "id=";
16const KEY_MODE: &str = "mode=";
17const KEY_PRETTY: &str = "pretty=";
18
19#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
21pub enum ReadlineControl {
22 Readline,
24 Control,
26}
27
28impl Display for ReadlineControl {
29 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
30 match self {
31 ReadlineControl::Readline => write!(f, "readline"),
32 ReadlineControl::Control => write!(f, "control"),
33 }
34 }
35}
36
37impl FromStr for ReadlineControl {
38 type Err = ();
39
40 fn from_str(s: &str) -> Result<Self, Self::Err> {
41 match s {
42 "readline" => Ok(ReadlineControl::Readline),
43 "control" => Ok(ReadlineControl::Control),
44 _ => Err(()),
45 }
46 }
47}
48impl ToArg for ReadlineControl {
49 fn to_arg(&self) -> &str {
50 match self {
51 ReadlineControl::Readline => "readline",
52 ReadlineControl::Control => "control",
53 }
54 }
55}
56#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
58pub struct Mon {
59 #[builder(into)]
61 chardev: ShellString,
62 #[builder(into)]
64 id: Option<ShellString>,
65 mode: Option<ReadlineControl>,
67 pretty: Option<OnOff>,
69}
70
71impl ToCommand for Mon {
72 fn command(&self) -> String {
73 ARG_MON.to_string()
74 }
75 fn to_args(&self) -> Vec<String> {
76 let mut args = vec![];
77 args.push(format!("{}{}", KEY_CHARDEV, self.chardev.as_ref()));
78 qao!(&self.id, args, KEY_ID);
79 qao!(&self.mode, args, KEY_MODE);
80 qao!(&self.pretty, args, KEY_PRETTY);
81 vec![args.join(DELIM_COMMA)]
82 }
83}
84
85impl FromStr for Mon {
86 type Err = ShellStringError;
87
88 fn from_str(s: &str) -> Result<Self, Self::Err> {
89 parse_mon(s).map_err(ShellStringError::new)
90 }
91}
92
93fn parse_mon(s: &str) -> Result<Mon, String> {
94 let mut parts = s.split(DELIM_COMMA);
95 let first = parts.next().ok_or_else(|| "empty monitor argument".to_string())?;
96
97 let chardev = if let Some(value) = first.strip_prefix(KEY_CHARDEV) {
98 ShellString::new(value)
99 } else if first.contains('=') {
100 return Err(format!("unsupported monitor option in first position: {first}"));
101 } else {
102 ShellString::new(first)
103 };
104
105 let mut mode = None;
106 let mut id = None;
107 let mut pretty = None;
108
109 for part in parts {
110 if part == "pretty" {
111 pretty = Some(OnOff::On);
112 continue;
113 }
114
115 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid monitor option: {part}"))?;
116 match key {
117 "chardev" => return Err("chardev= is only valid as the first -mon component".to_string()),
118 "id" => {
119 id = Some(ShellString::new(value));
120 }
121 "mode" => {
122 mode = Some(value.parse::<ReadlineControl>().map_err(|_| format!("invalid mode value: {value}"))?);
123 }
124 "pretty" => {
125 pretty = Some(value.parse::<OnOff>().map_err(|_| format!("invalid pretty value: {value}"))?);
126 }
127 other => return Err(format!("unsupported monitor option: {other}")),
128 }
129 }
130
131 Ok(Mon { chardev, id, mode, pretty })
132}