qemu_command_builder/args/
audiodev.rs1use crate::parsers::ARG_AUDIODEV;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6
7use crate::parsers::DELIM_COMMA;
8use crate::to_command::ToCommand;
9
10#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
12pub struct AudioDevProperty {
13 pub key: String,
15 pub value: Option<String>,
17}
18
19#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
25pub struct AudioDev {
26 driver: String,
28 props: Vec<AudioDevProperty>,
30}
31
32impl AudioDev {
33 pub fn new(driver: impl Into<String>) -> Self {
34 Self {
35 driver: driver.into(),
36 props: Vec::new(),
37 }
38 }
39
40 pub fn add_prop(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
41 self.props.push(AudioDevProperty {
42 key: key.into(),
43 value: Some(value.into()),
44 });
45 self
46 }
47
48 pub fn add_flag(&mut self, key: impl Into<String>) -> &mut Self {
49 self.props.push(AudioDevProperty { key: key.into(), value: None });
50 self
51 }
52}
53
54impl ToCommand for AudioDev {
55 fn command(&self) -> String {
56 ARG_AUDIODEV.to_string()
57 }
58 fn to_args(&self) -> Vec<String> {
59 let mut args = vec![self.driver.clone()];
60
61 let mut props = self.props.clone();
62 props.sort_by(|a, b| a.key.cmp(&b.key).then_with(|| a.value.cmp(&b.value)));
63
64 for prop in &props {
65 if let Some(value) = &prop.value {
66 args.push(format!("{}={}", prop.key, value));
67 } else {
68 args.push(prop.key.clone());
69 }
70 }
71 vec![args.join(DELIM_COMMA)]
72 }
73}
74
75impl FromStr for AudioDev {
76 type Err = String;
77
78 fn from_str(s: &str) -> Result<Self, Self::Err> {
79 let mut parts = s.split(DELIM_COMMA);
80 let first = parts.next().ok_or_else(|| "empty -audiodev argument".to_string())?;
81
82 let driver = if let Some(value) = first.strip_prefix("driver=") {
83 value.to_string()
84 } else if !first.contains('=') {
85 first.to_string()
86 } else {
87 return Err(format!("unsupported first -audiodev component: {first}"));
88 };
89
90 let mut props = Vec::new();
91 for part in parts {
92 if let Some((key, value)) = part.split_once('=') {
93 props.push(AudioDevProperty {
94 key: key.to_string(),
95 value: Some(value.to_string()),
96 });
97 } else {
98 props.push(AudioDevProperty { key: part.to_string(), value: None });
99 }
100 }
101
102 let has_id = props.iter().any(|prop| prop.key == "id");
103 if !has_id {
104 return Err("-audiodev requires id=".to_string());
105 }
106
107 Ok(Self { driver, props })
108 }
109}