qemu_command_builder/args/
g.rs1use crate::parsers::ARG_G;
2use crate::shell_string::ShellStringError;
3use crate::to_command::ToCommand;
4use bon::Builder;
5use proptest_derive::Arbitrary;
6use std::fmt::Display;
7use std::str::FromStr;
8
9#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
14pub struct G {
15 pub width: usize,
16 pub height: usize,
17 pub depth: Option<usize>,
18}
19
20impl Display for G {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "{}x{}", self.width, self.height)?;
23 if let Some(depth) = self.depth {
24 write!(f, "x{}", depth)?;
25 }
26 Ok(())
27 }
28}
29
30impl FromStr for G {
31 type Err = ShellStringError;
32
33 fn from_str(s: &str) -> Result<Self, Self::Err> {
34 parse_g(s).map_err(ShellStringError::new)
35 }
36}
37
38impl ToCommand for G {
39 fn command(&self) -> String {
40 ARG_G.to_string()
41 }
42
43 fn to_args(&self) -> Vec<String> {
44 vec![self.to_string()]
45 }
46}
47
48fn parse_g(s: &str) -> Result<G, String> {
49 if s.is_empty() {
50 return Err("empty -g option".to_string());
51 }
52
53 let mut parts = s.split('x');
54 let width = parse_dimension(parts.next(), "width")?;
55 let height = parse_dimension(parts.next(), "height")?;
56 let depth = parts.next().map(|value| parse_dimension(Some(value), "depth")).transpose()?;
57
58 if parts.next().is_some() {
59 return Err(format!("invalid -g option: expected WxH[xDEPTH], got {s}"));
60 }
61
62 Ok(G { width, height, depth })
63}
64
65fn parse_dimension(value: Option<&str>, name: &str) -> Result<usize, String> {
66 let value = value.ok_or_else(|| format!("missing -g {name}"))?;
67 if value.is_empty() {
68 return Err(format!("empty -g {name}"));
69 }
70 let parsed = value.parse::<usize>().map_err(|e| format!("invalid -g {name}: {e}"))?;
71 if parsed == 0 {
72 return Err(format!("-g {name} must be greater than zero"));
73 }
74 Ok(parsed)
75}