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