Skip to main content

qemu_command_builder/args/
tpmdev.rs

1use bon::Builder;
2use proptest_derive::Arbitrary;
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use crate::parsers::{ARG_TPMDEV, DELIM_COMMA};
7use crate::to_command::ToCommand;
8
9/// A `-tpmdev passthrough,...` backend.
10#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
11pub struct Passthrough {
12    id: String,
13    path: Option<PathBuf>,
14    cancel_path: Option<PathBuf>,
15}
16
17impl ToCommand for Passthrough {
18    fn to_args(&self) -> Vec<String> {
19        let mut args = vec!["passthrough".to_string(), format!("id={}", self.id.to_string())];
20
21        if let Some(path) = &self.path {
22            args.push(format!("path={}", path.display()));
23        }
24        if let Some(cancel_path) = &self.cancel_path {
25            args.push(format!("cancel-path={}", cancel_path.display()));
26        }
27
28        vec![args.join(DELIM_COMMA)]
29    }
30}
31
32impl FromStr for Passthrough {
33    type Err = String;
34
35    fn from_str(s: &str) -> Result<Self, Self::Err> {
36        let mut parts = s.split(DELIM_COMMA);
37        let backend = parts.next().ok_or_else(|| "empty -tpmdev argument".to_string())?;
38        if backend != "passthrough" {
39            return Err(format!("expected passthrough backend, got {backend}"));
40        }
41
42        let mut id = None;
43        let mut path = None;
44        let mut cancel_path = None;
45        for part in parts {
46            let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid passthrough option: {part}"))?;
47            match key {
48                "id" => id = Some(value.to_string()),
49                "path" => path = Some(PathBuf::from(value)),
50                "cancel-path" => cancel_path = Some(PathBuf::from(value)),
51                other => return Err(format!("unsupported passthrough option: {other}")),
52            }
53        }
54        Ok(Self {
55            id: id.ok_or_else(|| "passthrough requires id=".to_string())?,
56            path,
57            cancel_path,
58        })
59    }
60}
61
62/// A `-tpmdev emulator,...` backend.
63#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
64pub struct Emulator {
65    id: String,
66    chardev: String,
67}
68
69impl ToCommand for Emulator {
70    fn to_args(&self) -> Vec<String> {
71        vec![["emulator".to_string(), format!("id={}", self.id), format!("chardev={}", self.chardev)].join(DELIM_COMMA)]
72    }
73}
74
75impl FromStr for Emulator {
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 backend = parts.next().ok_or_else(|| "empty -tpmdev argument".to_string())?;
81        if backend != "emulator" {
82            return Err(format!("expected emulator backend, got {backend}"));
83        }
84
85        let mut id = None;
86        let mut chardev = None;
87        for part in parts {
88            let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid emulator option: {part}"))?;
89            match key {
90                "id" => id = Some(value.to_string()),
91                "chardev" => chardev = Some(value.to_string()),
92                other => return Err(format!("unsupported emulator option: {other}")),
93            }
94        }
95
96        Ok(Self {
97            id: id.ok_or_else(|| "emulator requires id=".to_string())?,
98            chardev: chardev.ok_or_else(|| "emulator requires chardev=".to_string())?,
99        })
100    }
101}
102
103#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
104pub enum TpmDev {
105    Passthrough(Passthrough),
106    Emulator(Emulator),
107}
108
109impl ToCommand for TpmDev {
110    fn command(&self) -> String {
111        ARG_TPMDEV.to_string()
112    }
113    fn to_args(&self) -> Vec<String> {
114        match self {
115            TpmDev::Passthrough(p) => p.to_args(),
116            TpmDev::Emulator(e) => e.to_args(),
117        }
118    }
119}
120
121impl FromStr for TpmDev {
122    type Err = String;
123
124    fn from_str(s: &str) -> Result<Self, Self::Err> {
125        if s.starts_with("passthrough,") || s == "passthrough" {
126            return Ok(Self::Passthrough(s.parse::<Passthrough>()?));
127        }
128        if s.starts_with("emulator,") || s == "emulator" {
129            return Ok(Self::Emulator(s.parse::<Emulator>()?));
130        }
131        Err(format!("unsupported tpmdev backend: {s}"))
132    }
133}