Skip to main content

qemu_command_builder/args/
g.rs

1use 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/// Initial graphical resolution and optional depth for QEMU `-g`.
10///
11/// QEMU accepts `-g WxH[xDEPTH]`, for example `800x600` or
12/// `1024x768x24`.
13#[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}