qemu_command_builder/args/
cpu.rs1use crate::parsers::ARG_CPU;
2use std::collections::{BTreeMap, BTreeSet};
3use std::str::FromStr;
4
5use bon::Builder;
6use proptest_derive::Arbitrary;
7use winnow::ascii::alphanumeric1;
8use winnow::combinator::{alt, opt, preceded, separated};
9use winnow::prelude::*;
10use winnow::token::{literal, take_while};
11
12use crate::args::cpu_flags::CPUFlag;
13use crate::args::cpu_type::{CpuTypeAarch64, CpuTypeX86_64};
14use crate::common::{OnOff, YesNo};
15use crate::parsers::{DELIM_COMMA, ascii_plus_more};
16use crate::shell_string::ShellStringError;
17use crate::{ToArg, ToCommand, qao};
18
19const KEY_MIGRATABLE: &str = "migratable=";
20
21#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
26pub struct CpuX86 {
27 cpu_type: CpuTypeX86_64,
28 migratable: Option<YesNo>,
29 flags: Option<BTreeMap<CPUFlag, OnOff>>,
30}
31
32impl CpuX86 {
33 pub fn new(cpu_type: CpuTypeX86_64) -> Self {
35 CpuX86 {
36 cpu_type,
37 migratable: None,
38 flags: None,
39 }
40 }
41
42 pub fn migratable(&mut self, state: YesNo) -> &mut Self {
44 self.migratable = Some(state);
45 self
46 }
47
48 pub fn flags(&mut self, flags: BTreeSet<(CPUFlag, OnOff)>) -> &mut Self {
54 let mut normalized = BTreeMap::new();
55 for (flag, state) in flags {
56 normalized.insert(flag, state);
57 }
58 self.flags = Some(normalized);
59 self
60 }
61}
62
63impl ToCommand for CpuX86 {
64 fn command(&self) -> String {
65 ARG_CPU.to_string()
66 }
67
68 fn to_args(&self) -> Vec<String> {
69 let mut args = vec![self.cpu_type.to_arg().to_string()];
70
71 qao!(&self.migratable, args, KEY_MIGRATABLE);
72 if let Some(flags) = &self.flags {
73 let flags: Vec<String> = flags
74 .iter()
75 .map(|(flag, state)| match state {
76 OnOff::On => flag.to_arg().to_string(),
77 OnOff::Off => format!("-{}", flag.to_arg()),
78 })
79 .collect();
80 if !flags.is_empty() {
81 args.push(flags.join(DELIM_COMMA));
82 }
83 }
84
85 vec![args.join(DELIM_COMMA)]
86 }
87}
88
89impl FromStr for CpuX86 {
90 type Err = ShellStringError;
91
92 fn from_str(s: &str) -> Result<Self, Self::Err> {
93 cpu_x86_64.parse(s).map_err(|e| ShellStringError::from_parse(e))
94 }
95}
96
97fn cpu_type(s: &mut &str) -> ModalResult<CpuTypeX86_64> {
98 ascii_plus_more.parse_to::<CpuTypeX86_64>().parse_next(s)
99}
100
101fn migratable_item(s: &mut &str) -> ModalResult<YesNo> {
102 let _ = literal(KEY_MIGRATABLE).parse_next(s)?;
103 alphanumeric1.parse_to::<YesNo>().parse_next(s)
104}
105
106pub fn cpu_flag_parser<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
107 take_while(1.., |c: char| c.is_ascii_alphanumeric() || c == '-' || c == '.').parse_next(input)
108}
109
110fn cpu_flag(s: &mut &str) -> ModalResult<(CPUFlag, OnOff)> {
111 let state = opt(literal('-')).parse_next(s)?;
112 let flag = cpu_flag_parser.parse_to::<CPUFlag>().parse_next(s)?;
113 Ok((
114 flag,
115 match state {
116 None => OnOff::On,
117 Some(_) => OnOff::Off,
118 },
119 ))
120}
121
122fn cpu_flag_property(s: &mut &str) -> ModalResult<(CPUFlag, OnOff)> {
123 let flag = cpu_flag_parser.parse_to::<CPUFlag>().parse_next(s)?;
124 let _ = literal("=").parse_next(s)?;
125 let state = alphanumeric1.parse_to::<OnOff>().parse_next(s)?;
126 Ok((flag, state))
127}
128
129#[derive(Debug, Clone, Eq, PartialEq)]
130enum CpuX86Item {
131 Migratable(YesNo),
132 Flag(CPUFlag, OnOff),
133}
134
135fn cpu_x86_item(s: &mut &str) -> ModalResult<CpuX86Item> {
136 alt((
137 migratable_item.map(CpuX86Item::Migratable),
138 cpu_flag_property.map(|(flag, state)| CpuX86Item::Flag(flag, state)),
139 cpu_flag.map(|(flag, state)| CpuX86Item::Flag(flag, state)),
140 ))
141 .parse_next(s)
142}
143
144fn cpu_x86_64(s: &mut &str) -> ModalResult<CpuX86> {
145 let cpu_type = cpu_type(s)?;
146 let items: Option<Vec<CpuX86Item>> = opt(preceded(literal(DELIM_COMMA), separated(1.., cpu_x86_item, DELIM_COMMA))).parse_next(s)?;
147 let mut migratable = None;
148 let mut flags = BTreeMap::new();
149
150 if let Some(items) = items {
151 for item in items {
152 match item {
153 CpuX86Item::Migratable(value) => migratable = Some(value),
154 CpuX86Item::Flag(flag, state) => {
155 flags.insert(flag, state);
156 }
157 }
158 }
159 }
160
161 let flags = (!flags.is_empty()).then_some(flags);
162 Ok(CpuX86 { cpu_type, migratable, flags })
163}
164
165#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
167pub struct CpuAarch64 {
168 pub cpu_type: CpuTypeAarch64,
169}
170
171impl ToCommand for CpuAarch64 {
172 fn command(&self) -> String {
173 ARG_CPU.to_string()
174 }
175 fn to_args(&self) -> Vec<String> {
176 vec![self.cpu_type.to_command().join("")]
177 }
178}
179
180fn cpu_type_aarch64(s: &mut &str) -> ModalResult<CpuTypeAarch64> {
181 ascii_plus_more.parse_to::<CpuTypeAarch64>().parse_next(s)
182}
183
184impl FromStr for CpuAarch64 {
185 type Err = ShellStringError;
186
187 fn from_str(s: &str) -> Result<Self, Self::Err> {
188 cpu_aarch64.parse(s).map_err(|e| ShellStringError::from_parse(e))
189 }
190}
191
192fn cpu_aarch64(s: &mut &str) -> ModalResult<CpuAarch64> {
193 let cpu_type = cpu_type_aarch64.parse_next(s)?;
194 Ok(CpuAarch64 { cpu_type })
195}