Skip to main content

qemu_command_builder/args/
fw_cfg.rs

1use bon::Builder;
2use proptest_derive::Arbitrary;
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use crate::parsers::{ARG_FW_CFG, DELIM_COMMA};
7use crate::to_command::ToCommand;
8
9#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
10pub enum StringOrPathBuf {
11    String(String),
12    PathBuf(PathBuf),
13}
14
15#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
16pub struct FwCfg {
17    /// The fw_cfg entry name.
18    name: Option<String>,
19    /// Either inline string contents or a file path.
20    data: Option<StringOrPathBuf>,
21}
22
23impl ToCommand for FwCfg {
24    fn command(&self) -> String {
25        ARG_FW_CFG.to_string()
26    }
27    fn to_args(&self) -> Vec<String> {
28        let mut args = vec![];
29        if let Some(name) = &self.name {
30            args.push(format!("name={}", name));
31        }
32        if let Some(data) = &self.data {
33            match &data {
34                StringOrPathBuf::String(string) => {
35                    args.push(format!("string={}", string));
36                }
37                StringOrPathBuf::PathBuf(path) => {
38                    args.push(format!("file={}", path.display()));
39                }
40            }
41        }
42
43        vec![args.join(DELIM_COMMA)]
44    }
45}
46
47impl FromStr for FwCfg {
48    type Err = String;
49
50    fn from_str(s: &str) -> Result<Self, Self::Err> {
51        let mut parts = s.split(DELIM_COMMA);
52        let first = parts.next().ok_or_else(|| "empty -fw_cfg argument".to_string())?;
53
54        let mut name = None;
55        let mut data = None;
56
57        if let Some(value) = first.strip_prefix("name=") {
58            name = Some(value.to_string());
59        } else if !first.contains('=') {
60            name = Some(first.to_string());
61        } else {
62            let (key, value) = first.split_once('=').ok_or_else(|| format!("invalid -fw_cfg option: {first}"))?;
63            match key {
64                "file" => data = Some(StringOrPathBuf::PathBuf(PathBuf::from(value))),
65                "string" => data = Some(StringOrPathBuf::String(value.to_string())),
66                other => return Err(format!("unsupported -fw_cfg option: {other}")),
67            }
68        }
69
70        for part in parts {
71            let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid -fw_cfg option: {part}"))?;
72            match key {
73                "name" => name = Some(value.to_string()),
74                "file" => data = Some(StringOrPathBuf::PathBuf(PathBuf::from(value))),
75                "string" => data = Some(StringOrPathBuf::String(value.to_string())),
76                other => return Err(format!("unsupported -fw_cfg option: {other}")),
77            }
78        }
79
80        Ok(Self { name, data })
81    }
82}