Skip to main content

qemu_command_builder/args/
action.rs

1use crate::parsers::ARG_ACTION;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6
7use crate::parsers::DELIM_COMMA;
8use crate::to_command::{ToArg, ToCommand};
9
10const KEY_REBOOT: &str = "reboot=";
11const KEY_SHUTDOWN: &str = "shutdown=";
12const KEY_PANIC: &str = "panic=";
13const KEY_WATCHDOG: &str = "watchdog=";
14
15const VAL_RESET: &str = "reset";
16const VAL_SHUTDOWN: &str = "shutdown";
17const VAL_POWEROFF: &str = "poweroff";
18const VAL_PAUSE: &str = "pause";
19const VAL_EXIT_FAILURE: &str = "exit-failure";
20const VAL_NONE: &str = "none";
21const VAL_INJECT_NMI: &str = "inject-nmi";
22const VAL_DEBUG: &str = "debug";
23
24/// QEMU `reboot=` actions for `-action`.
25#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Arbitrary)]
26pub enum RebootAction {
27    #[default]
28    Reset,
29    Shutdown,
30}
31
32impl ToArg for RebootAction {
33    fn to_arg(&self) -> &str {
34        match self {
35            RebootAction::Reset => VAL_RESET,
36            RebootAction::Shutdown => VAL_SHUTDOWN,
37        }
38    }
39}
40
41impl FromStr for RebootAction {
42    type Err = String;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        match s {
46            VAL_RESET => Ok(Self::Reset),
47            VAL_SHUTDOWN => Ok(Self::Shutdown),
48            _ => Err(format!("invalid reboot action: {s}")),
49        }
50    }
51}
52
53/// QEMU `shutdown=` actions for `-action`.
54#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Arbitrary)]
55pub enum ShutdownAction {
56    #[default]
57    PowerOff,
58    Pause,
59}
60
61impl ToArg for ShutdownAction {
62    fn to_arg(&self) -> &str {
63        match self {
64            ShutdownAction::PowerOff => VAL_POWEROFF,
65            ShutdownAction::Pause => VAL_PAUSE,
66        }
67    }
68}
69
70impl FromStr for ShutdownAction {
71    type Err = String;
72
73    fn from_str(s: &str) -> Result<Self, Self::Err> {
74        match s {
75            VAL_POWEROFF => Ok(Self::PowerOff),
76            VAL_PAUSE => Ok(Self::Pause),
77            _ => Err(format!("invalid shutdown action: {s}")),
78        }
79    }
80}
81
82/// QEMU `panic=` actions for `-action`.
83#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Arbitrary)]
84pub enum PanicAction {
85    Pause,
86    #[default]
87    Shutdown,
88    ExitFailure,
89    None,
90}
91
92impl ToArg for PanicAction {
93    fn to_arg(&self) -> &str {
94        match self {
95            PanicAction::Pause => VAL_PAUSE,
96            PanicAction::Shutdown => VAL_SHUTDOWN,
97            PanicAction::ExitFailure => VAL_EXIT_FAILURE,
98            PanicAction::None => VAL_NONE,
99        }
100    }
101}
102
103impl FromStr for PanicAction {
104    type Err = String;
105
106    fn from_str(s: &str) -> Result<Self, Self::Err> {
107        match s {
108            VAL_PAUSE => Ok(Self::Pause),
109            VAL_SHUTDOWN => Ok(Self::Shutdown),
110            VAL_EXIT_FAILURE => Ok(Self::ExitFailure),
111            VAL_NONE => Ok(Self::None),
112            _ => Err(format!("invalid panic action: {s}")),
113        }
114    }
115}
116
117/// QEMU `watchdog=` actions for `-action` and `-watchdog-action`.
118#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Arbitrary)]
119pub enum WatchdogAction {
120    #[default]
121    Reset,
122    Shutdown,
123    PowerOff,
124    InjectNmi,
125    Pause,
126    Debug,
127    None,
128}
129
130impl ToArg for WatchdogAction {
131    fn to_arg(&self) -> &str {
132        match self {
133            WatchdogAction::Reset => VAL_RESET,
134            WatchdogAction::Shutdown => VAL_SHUTDOWN,
135            WatchdogAction::PowerOff => VAL_POWEROFF,
136            WatchdogAction::InjectNmi => VAL_INJECT_NMI,
137            WatchdogAction::Pause => VAL_PAUSE,
138            WatchdogAction::Debug => VAL_DEBUG,
139            WatchdogAction::None => VAL_NONE,
140        }
141    }
142}
143
144impl FromStr for WatchdogAction {
145    type Err = String;
146
147    fn from_str(s: &str) -> Result<Self, Self::Err> {
148        match s {
149            VAL_RESET => Ok(Self::Reset),
150            VAL_SHUTDOWN => Ok(Self::Shutdown),
151            VAL_POWEROFF => Ok(Self::PowerOff),
152            VAL_INJECT_NMI => Ok(Self::InjectNmi),
153            VAL_PAUSE => Ok(Self::Pause),
154            VAL_DEBUG => Ok(Self::Debug),
155            VAL_NONE => Ok(Self::None),
156            _ => Err(format!("invalid watchdog action: {s}")),
157        }
158    }
159}
160
161/// QEMU `-action` event handlers.
162///
163/// QEMU accepts a comma-separated list of event assignments such as
164/// `reboot=shutdown,shutdown=pause`. This type preserves those assignments in
165/// canonical field order.
166#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
167pub struct Action {
168    reboot: Option<RebootAction>,
169    shutdown: Option<ShutdownAction>,
170    panic: Option<PanicAction>,
171    watchdog: Option<WatchdogAction>,
172}
173
174impl Action {
175    /// Creates an empty `-action` configuration.
176    pub fn new() -> Self {
177        Self::default()
178    }
179}
180
181impl ToCommand for Action {
182    fn has_args(&self) -> bool {
183        self.reboot.is_some() || self.shutdown.is_some() || self.panic.is_some() || self.watchdog.is_some()
184    }
185
186    fn command(&self) -> String {
187        ARG_ACTION.to_string()
188    }
189
190    fn to_args(&self) -> Vec<String> {
191        let mut args = vec![];
192        if let Some(action) = &self.reboot {
193            args.push(format!("{}{}", KEY_REBOOT, action.to_arg()));
194        }
195        if let Some(action) = &self.shutdown {
196            args.push(format!("{}{}", KEY_SHUTDOWN, action.to_arg()));
197        }
198        if let Some(action) = &self.panic {
199            args.push(format!("{}{}", KEY_PANIC, action.to_arg()));
200        }
201        if let Some(action) = &self.watchdog {
202            args.push(format!("{}{}", KEY_WATCHDOG, action.to_arg()));
203        }
204        vec![args.join(DELIM_COMMA)]
205    }
206}
207
208impl FromStr for Action {
209    type Err = String;
210
211    fn from_str(s: &str) -> Result<Self, Self::Err> {
212        let mut action = Action::default();
213
214        for part in s.split(DELIM_COMMA) {
215            let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid action option: {part}"))?;
216            match key {
217                "reboot" => action.reboot = Some(value.parse::<RebootAction>()?),
218                "shutdown" => action.shutdown = Some(value.parse::<ShutdownAction>()?),
219                "panic" => action.panic = Some(value.parse::<PanicAction>()?),
220                "watchdog" => action.watchdog = Some(value.parse::<WatchdogAction>()?),
221                other => return Err(format!("unsupported action key: {other}")),
222            }
223        }
224
225        Ok(action)
226    }
227}