qemu_command_builder/args/
icount.rs1use std::path::PathBuf;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6
7use crate::common::OnOff;
8use crate::parsers::{ARG_ICOUNT, DELIM_COMMA};
9use crate::to_command::{ToArg, ToCommand};
10
11#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
12pub enum Shift {
13 N(usize),
14 Auto,
15}
16
17#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
19pub enum RecordReplay {
20 Record,
21 Replay,
22}
23
24impl ToArg for RecordReplay {
25 fn to_arg(&self) -> &str {
26 match self {
27 RecordReplay::Record => "record",
28 RecordReplay::Replay => "replay",
29 }
30 }
31}
32#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
34pub struct Icount {
35 shift: Option<Shift>,
37 align: Option<OnOff>,
39 sleep: Option<OnOff>,
41 rr: Option<RecordReplay>,
43 rrfile: Option<PathBuf>,
45 rrsnapshot: Option<String>,
47}
48
49impl ToCommand for Icount {
50 fn command(&self) -> String {
51 ARG_ICOUNT.to_string()
52 }
53 fn to_args(&self) -> Vec<String> {
54 let mut args = vec![];
55
56 if let Some(shift) = &self.shift {
57 match shift {
58 Shift::N(size) => {
59 args.push(format!("shift={}", size));
60 }
61 Shift::Auto => {
62 args.push("shift=auto".to_string());
63 }
64 }
65 }
66 if let Some(align) = &self.align {
67 args.push(format!("align={}", align.to_arg()));
68 }
69 if let Some(sleep) = &self.sleep {
70 args.push(format!("sleep={}", sleep.to_arg()));
71 }
72 if let Some(rr) = &self.rr {
73 args.push(format!("rr={}", rr.to_arg()));
74 }
75 if let Some(rrfile) = &self.rrfile {
76 args.push(format!("rrfile={}", rrfile.display()));
77 }
78 if let Some(rrsnapshot) = &self.rrsnapshot {
79 args.push(format!("rrsnapshot={}", rrsnapshot));
80 }
81
82 vec![args.join(DELIM_COMMA)]
83 }
84}
85
86impl FromStr for Icount {
87 type Err = String;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 let mut shift = None;
91 let mut align = None;
92 let mut sleep = None;
93 let mut rr = None;
94 let mut rrfile = None;
95 let mut rrsnapshot = None;
96
97 for part in s.split(DELIM_COMMA).filter(|part| !part.is_empty()) {
98 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid -icount option: {part}"))?;
99 match key {
100 "shift" => {
101 shift = Some(if value == "auto" {
102 Shift::Auto
103 } else {
104 Shift::N(value.parse::<usize>().map_err(|e| e.to_string())?)
105 })
106 }
107 "align" => align = Some(value.parse::<OnOff>().map_err(|_| format!("invalid align value: {value}"))?),
108 "sleep" => sleep = Some(value.parse::<OnOff>().map_err(|_| format!("invalid sleep value: {value}"))?),
109 "rr" => {
110 rr = Some(match value {
111 "record" => RecordReplay::Record,
112 "replay" => RecordReplay::Replay,
113 _ => return Err(format!("invalid rr value: {value}")),
114 })
115 }
116 "rrfile" => rrfile = Some(PathBuf::from(value)),
117 "rrsnapshot" => rrsnapshot = Some(value.to_string()),
118 other => return Err(format!("unsupported -icount option: {other}")),
119 }
120 }
121
122 Ok(Self {
123 shift,
124 align,
125 sleep,
126 rr,
127 rrfile,
128 rrsnapshot,
129 })
130 }
131}