Skip to main content

qemu_command_builder/args/
compact.rs

1use crate::parsers::ARG_COMPAT;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6
7use crate::parsers::DELIM_COMMA;
8use crate::to_command::ToArg;
9use crate::to_command::ToCommand;
10
11/// Input policy values for `-compat` compatibility knobs.
12#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
13pub enum AcceptRejectCrash {
14    Accept,
15    Reject,
16    Crash,
17}
18
19impl ToArg for AcceptRejectCrash {
20    fn to_arg(&self) -> &str {
21        match self {
22            AcceptRejectCrash::Accept => "accept",
23            AcceptRejectCrash::Reject => "reject",
24            AcceptRejectCrash::Crash => "crash",
25        }
26    }
27}
28
29/// Output policy values for `-compat` compatibility knobs.
30#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
31pub enum AcceptHide {
32    Accept,
33    Hide,
34}
35impl ToArg for AcceptHide {
36    fn to_arg(&self) -> &str {
37        match self {
38            AcceptHide::Accept => "accept",
39            AcceptHide::Hide => "hide",
40        }
41    }
42}
43/// `-compat deprecated-input=...,deprecated-output=...`.
44#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
45pub struct DeprecatedInput {
46    deprecated_input: AcceptRejectCrash,
47    deprecated_output: AcceptHide,
48}
49
50/// `-compat unstable-input=...,unstable-output=...`.
51#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
52pub struct UnstableInput {
53    unstable_input: AcceptRejectCrash,
54    unstable_output: AcceptHide,
55}
56
57/// A QEMU `-compat` definition.
58#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
59pub enum Compact {
60    DeprecatedInput(DeprecatedInput),
61    UnstableInput(UnstableInput),
62}
63
64impl ToCommand for Compact {
65    fn command(&self) -> String {
66        ARG_COMPAT.to_string()
67    }
68
69    fn to_args(&self) -> Vec<String> {
70        match self {
71            Compact::DeprecatedInput(deprecated_input) => {
72                let mut args = vec![];
73                args.push(format!("deprecated-input={}", deprecated_input.deprecated_input.to_arg()));
74                args.push(format!("deprecated-output={}", deprecated_input.deprecated_output.to_arg()));
75                vec![args.join(DELIM_COMMA)]
76            }
77            Compact::UnstableInput(unstable_input) => {
78                let mut args = vec![];
79                args.push(format!("unstable-input={}", unstable_input.unstable_input.to_arg()));
80                args.push(format!("unstable-output={}", unstable_input.unstable_output.to_arg()));
81                vec![args.join(DELIM_COMMA)]
82            }
83        }
84    }
85}
86
87impl FromStr for Compact {
88    type Err = String;
89
90    fn from_str(s: &str) -> Result<Self, Self::Err> {
91        let mut deprecated_input = None;
92        let mut deprecated_output = None;
93        let mut unstable_input = None;
94        let mut unstable_output = None;
95
96        for part in s.split(DELIM_COMMA).filter(|part| !part.is_empty()) {
97            let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid -compat option: {part}"))?;
98            match key {
99                "deprecated-input" => {
100                    deprecated_input = Some(parse_accept_reject_crash(value)?);
101                }
102                "deprecated-output" => {
103                    deprecated_output = Some(parse_accept_hide(value)?);
104                }
105                "unstable-input" => {
106                    unstable_input = Some(parse_accept_reject_crash(value)?);
107                }
108                "unstable-output" => {
109                    unstable_output = Some(parse_accept_hide(value)?);
110                }
111                other => return Err(format!("unsupported -compat option: {other}")),
112            }
113        }
114
115        match (deprecated_input, deprecated_output, unstable_input, unstable_output) {
116            (Some(input), Some(output), None, None) => Ok(Self::DeprecatedInput(DeprecatedInput {
117                deprecated_input: input,
118                deprecated_output: output,
119            })),
120            (None, None, Some(input), Some(output)) => Ok(Self::UnstableInput(UnstableInput {
121                unstable_input: input,
122                unstable_output: output,
123            })),
124            (Some(_), None, None, None) | (None, Some(_), None, None) => Err("both deprecated-input and deprecated-output are required together".to_string()),
125            (None, None, Some(_), None) | (None, None, None, Some(_)) => Err("both unstable-input and unstable-output are required together".to_string()),
126            (None, None, None, None) => Err("empty -compat argument".to_string()),
127            _ => Err("cannot mix deprecated-* and unstable-* compat policies in one Compact value".to_string()),
128        }
129    }
130}
131
132fn parse_accept_reject_crash(value: &str) -> Result<AcceptRejectCrash, String> {
133    match value {
134        "accept" => Ok(AcceptRejectCrash::Accept),
135        "reject" => Ok(AcceptRejectCrash::Reject),
136        "crash" => Ok(AcceptRejectCrash::Crash),
137        _ => Err(format!("invalid compat input policy: {value}")),
138    }
139}
140
141fn parse_accept_hide(value: &str) -> Result<AcceptHide, String> {
142    match value {
143        "accept" => Ok(AcceptHide::Accept),
144        "hide" => Ok(AcceptHide::Hide),
145        _ => Err(format!("invalid compat output policy: {value}")),
146    }
147}