qemu_command_builder/args/
object.rs1use crate::parsers::{ARG_OBJECT, DELIM_COMMA};
2use crate::to_command::ToCommand;
3use bon::Builder;
4use proptest_derive::Arbitrary;
5use std::str::FromStr;
6
7#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
9pub struct Object {
10 typename: String,
12 properties: Vec<(String, String)>,
14 json: Option<String>,
16}
17
18impl Object {
19 pub fn new<S: AsRef<str>>(typename: S) -> Self {
20 Object {
21 typename: typename.as_ref().to_string(),
22 properties: Default::default(),
23 json: None,
24 }
25 }
26 pub fn from_json(json: impl Into<String>) -> Result<Self, String> {
28 let json = json.into();
29 if !json.trim().starts_with('{') || !json.trim().ends_with('}') {
30 return Err("-object JSON must be a JSON object".to_string());
31 }
32 Ok(Self {
33 typename: String::new(),
34 properties: Vec::new(),
35 json: Some(json),
36 })
37 }
38 pub fn add_prop<S: AsRef<str>>(&mut self, key: S, value: S) -> &mut Self {
39 self.properties.push((key.as_ref().to_string(), value.as_ref().to_string()));
40 self
41 }
42
43 pub fn monitor_hmp(id: impl AsRef<str>, chardev: impl AsRef<str>) -> Self {
45 let mut value = Self::new("monitor-hmp");
46 value.add_prop("id", id.as_ref()).add_prop("chardev", chardev.as_ref());
47 value
48 }
49
50 pub fn monitor_qmp(id: impl AsRef<str>, chardev: impl AsRef<str>) -> Self {
52 let mut value = Self::new("monitor-qmp");
53 value.add_prop("id", id.as_ref()).add_prop("chardev", chardev.as_ref());
54 value
55 }
56
57 pub fn tdx_guest(id: impl AsRef<str>) -> Self {
60 let mut value = Self::new("tdx-guest");
61 value.add_prop("id", id.as_ref());
62 value
63 }
64
65 pub fn iothread(id: impl AsRef<str>) -> Self {
68 let mut value = Self::new("iothread");
69 value.add_prop("id", id.as_ref());
70 value
71 }
72}
73
74impl ToCommand for Object {
75 fn command(&self) -> String {
76 ARG_OBJECT.to_string()
77 }
78 fn to_args(&self) -> Vec<String> {
79 if let Some(json) = &self.json {
80 return vec![json.clone()];
81 }
82 let mut args = vec![self.typename.clone()];
83
84 for (prop_key, prop_value) in &self.properties {
85 args.push(format!("{}={}", prop_key, prop_value));
86 }
87 vec![args.join(DELIM_COMMA)]
88 }
89}
90
91impl FromStr for Object {
92 type Err = String;
93
94 fn from_str(s: &str) -> Result<Self, Self::Err> {
95 if s.trim().starts_with('{') {
96 return Self::from_json(s);
97 }
98 let mut parts = s.split(DELIM_COMMA);
99 let typename = parts.next().ok_or_else(|| "empty -object argument".to_string())?.to_string();
100
101 let mut properties = Vec::new();
102 for part in parts {
103 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid -object property: {part}"))?;
104 properties.push((key.to_string(), value.to_string()));
105 }
106
107 Ok(Self { typename, properties, json: None })
108 }
109}