qemu_command_builder/args/
acpitable.rs1use crate::parsers::ARG_ACPITABLE;
2use std::fmt::{Display, Formatter};
3use std::str::FromStr;
4
5use crate::parsers::{DELIM_COLON, DELIM_COMMA};
6use crate::qao;
7use crate::shell_path::ShellPath;
8use crate::shell_string::ShellString;
9use crate::to_command::ToCommand;
10use bon::Builder;
11use proptest_derive::Arbitrary;
12
13const KEY_SIG: &str = "sig=";
14const KEY_REV: &str = "rev=";
15const KEY_OEM_ID: &str = "oem_id=";
16const KEY_OEM_TABLE_ID: &str = "oem_table_id=";
17const KEY_OEM_REV: &str = "oem_rev=";
18const KEY_ASL_COMPILER_ID: &str = "asl_compiler_id=";
19const KEY_ASL_COMPILER_REV: &str = "asl_compiler_rev=";
20const KEY_FILE: &str = "file=";
21const KEY_DATA: &str = "data=";
22
23#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
29pub enum AcpiTableData {
30 File(Vec<ShellPath>),
32 Data(Vec<ShellPath>),
34}
35
36impl AcpiTableData {
37 fn key(&self) -> &'static str {
38 match self {
39 AcpiTableData::File(_) => KEY_FILE,
40 AcpiTableData::Data(_) => KEY_DATA,
41 }
42 }
43
44 fn files(&self) -> &[ShellPath] {
45 match self {
46 AcpiTableData::File(files) | AcpiTableData::Data(files) => files,
47 }
48 }
49}
50
51impl Display for AcpiTableData {
52 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53 let files = self.files().iter().map(|p| p.as_ref()).collect::<Vec<_>>().join(DELIM_COLON);
54 write!(f, "{}{}", self.key(), files)
55 }
56}
57
58#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
68pub struct AcpiTable {
69 sig: Option<ShellString>,
71 rev: Option<usize>,
73 oem_id: Option<ShellString>,
75 oem_table_id: Option<ShellString>,
77 oem_rev: Option<usize>,
79 asl_compiler_id: Option<ShellString>,
81 asl_compiler_rev: Option<usize>,
83 #[proptest(filter = "acpi_table_data_is_nonempty")]
84 data: Option<AcpiTableData>,
86}
87
88fn acpi_table_data_is_nonempty(segment: &Option<AcpiTableData>) -> bool {
89 match segment {
90 None => false,
91 Some(f) => !f.files().is_empty(),
92 }
93}
94
95impl AcpiTable {
96 pub fn new() -> Self {
98 Self::default()
99 }
100}
101
102impl ToCommand for AcpiTable {
103 fn has_args(&self) -> bool {
104 self.sig.is_some()
105 || self.rev.is_some()
106 || self.oem_id.is_some()
107 || self.oem_table_id.is_some()
108 || self.oem_rev.is_some()
109 || self.asl_compiler_id.is_some()
110 || self.asl_compiler_rev.is_some()
111 || self.data.is_some()
112 }
113 fn command(&self) -> String {
114 ARG_ACPITABLE.to_string()
115 }
116 fn to_args(&self) -> Vec<String> {
117 let mut args = vec![];
118 if let Some(sig) = &self.sig {
119 args.push(format!("{}{}", KEY_SIG, sig.as_ref()));
120 }
121 qao!(&self.rev, args, KEY_REV);
122 if let Some(oem_id) = &self.oem_id {
123 args.push(format!("{}{}", KEY_OEM_ID, oem_id.as_ref()));
124 }
125 if let Some(oem_table_id) = &self.oem_table_id {
126 args.push(format!("{}{}", KEY_OEM_TABLE_ID, oem_table_id.as_ref()));
127 }
128 qao!(&self.oem_rev, args, KEY_OEM_REV);
129 if let Some(asl_compiler_id) = &self.asl_compiler_id {
130 args.push(format!("{}{}", KEY_ASL_COMPILER_ID, asl_compiler_id.as_ref()));
131 }
132 qao!(&self.asl_compiler_rev, args, KEY_ASL_COMPILER_REV);
133
134 if let Some(data) = &self.data {
135 args.push(data.to_string());
136 }
137
138 vec![args.join(DELIM_COMMA)]
139 }
140}
141
142impl FromStr for AcpiTable {
143 type Err = String;
144
145 fn from_str(s: &str) -> Result<Self, Self::Err> {
146 let mut table = AcpiTable::default();
147
148 for part in s.split(DELIM_COMMA) {
149 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid acpitable option: {part}"))?;
150 match key {
151 "sig" => table.sig = Some(ShellString::from_str(value)?),
152 "rev" => table.rev = Some(value.parse::<usize>().map_err(|e| e.to_string())?),
153 "oem_id" => table.oem_id = Some(ShellString::from_str(value)?),
154 "oem_table_id" => table.oem_table_id = Some(ShellString::from_str(value)?),
155 "oem_rev" => table.oem_rev = Some(value.parse::<usize>().map_err(|e| e.to_string())?),
156 "asl_compiler_id" => table.asl_compiler_id = Some(ShellString::from_str(value)?),
157 "asl_compiler_rev" => table.asl_compiler_rev = Some(value.parse::<usize>().map_err(|e| e.to_string())?),
158 "file" => table.data = Some(parse_data_list(value, false)),
159 "data" => table.data = Some(parse_data_list(value, true)),
160 other => return Err(format!("unsupported acpitable option: {other}")),
161 }
162 }
163
164 Ok(table)
165 }
166}
167
168fn parse_data_list(value: &str, is_data: bool) -> AcpiTableData {
169 let files = value.split(DELIM_COLON).map(ShellPath::from).collect::<Vec<_>>();
170 if is_data { AcpiTableData::Data(files) } else { AcpiTableData::File(files) }
171}