qemu_command_builder/args/
tpmdev.rs1use 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#[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#[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}