qemu_command_builder/args/
device.rs1use crate::parsers::ARG_DEVICE;
2use crate::parsers::DELIM_COMMA;
3use crate::shell_string::ShellString;
4use crate::to_command::ToCommand;
5use bon::Builder;
6use proptest_derive::Arbitrary;
7use std::collections::BTreeMap;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
12pub struct DeviceProperty {
13 key: ShellString,
14 value: Option<ShellString>,
15}
16
17impl DeviceProperty {
18 pub fn with_value(key: impl AsRef<str>, value: impl AsRef<str>) -> Self {
20 Self {
21 key: ShellString::from(key.as_ref()),
22 value: Some(ShellString::from(value.as_ref())),
23 }
24 }
25
26 pub fn flag(key: impl AsRef<str>) -> Self {
28 Self {
29 key: ShellString::from(key.as_ref()),
30 value: None,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
40pub struct Device {
41 device: ShellString,
42 #[builder(default)]
43 properties: BTreeMap<ShellString, Option<ShellString>>,
44}
45
46impl Device {
47 pub fn new<S: AsRef<str>>(device: S) -> Self {
49 Device {
50 device: ShellString::from(device.as_ref()),
51 properties: Default::default(),
52 }
53 }
54
55 pub fn add_prop<K: AsRef<str>, V: AsRef<str>>(&mut self, key: K, value: V) -> &mut Self {
57 self.properties.insert(ShellString::from(key.as_ref()), Some(ShellString::from(value.as_ref())));
58 self
59 }
60
61 pub fn add_flag<K: AsRef<str>>(&mut self, key: K) -> &mut Self {
63 self.properties.insert(ShellString::from(key.as_ref()), None);
64 self
65 }
66}
67
68impl ToCommand for Device {
69 fn command(&self) -> String {
70 ARG_DEVICE.to_string()
71 }
72
73 fn to_args(&self) -> Vec<String> {
74 let mut args = vec![self.device.as_ref().to_string()];
75
76 for (prop_key, prop_value) in &self.properties {
77 match prop_value {
78 Some(value) => args.push(format!("{}={}", prop_key.as_ref(), value.as_ref())),
79 None => args.push(prop_key.as_ref().to_string()),
80 }
81 }
82
83 vec![args.join(DELIM_COMMA)]
84 }
85}
86
87impl FromStr for Device {
88 type Err = String;
89
90 fn from_str(s: &str) -> Result<Self, Self::Err> {
91 let mut parts = s.split(DELIM_COMMA);
92 let device = parts.next().ok_or_else(|| "empty device argument".to_string())?;
93 if device.is_empty() {
94 return Err("missing device driver".to_string());
95 }
96
97 let mut properties = BTreeMap::new();
98 for part in parts {
99 match part.split_once('=') {
100 Some((key, value)) => {
101 properties.insert(ShellString::from(key), Some(ShellString::from_str(value)?));
102 }
103 None => {
104 properties.insert(ShellString::from(part), None);
105 }
106 }
107 }
108
109 Ok(Device {
110 device: ShellString::from(device),
111 properties,
112 })
113 }
114}