Skip to main content

qemu_command_builder/args/
trace.rs

1use crate::parsers::{ARG_TRACE, DELIM_COMMA};
2use crate::to_command::ToCommand;
3use bon::Builder;
4use proptest_derive::Arbitrary;
5use std::path::PathBuf;
6use std::str::FromStr;
7
8/// A QEMU `-trace [[enable=]pattern][,events=file][,file=file]` definition.
9#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
10pub struct Trace {
11    /// An enabled trace event pattern.
12    enable: Option<String>,
13    /// The events file.
14    events: Option<PathBuf>,
15    /// The trace output file.
16    file: Option<PathBuf>,
17}
18
19impl ToCommand for Trace {
20    fn command(&self) -> String {
21        ARG_TRACE.to_string()
22    }
23    fn to_args(&self) -> Vec<String> {
24        let mut args = vec![];
25
26        if let Some(enable) = &self.enable {
27            args.push(format!("enable={}", enable));
28        }
29        if let Some(events) = &self.events {
30            args.push(format!("events={}", events.display()));
31        }
32        if let Some(file) = &self.file {
33            args.push(format!("file={}", file.display()));
34        }
35        vec![args.join(DELIM_COMMA)]
36    }
37}
38
39impl FromStr for Trace {
40    type Err = String;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        let mut enable = None;
44        let mut events = None;
45        let mut file = None;
46
47        let mut parts = s.split(DELIM_COMMA);
48        if let Some(first) = parts.next()
49            && !first.is_empty()
50        {
51            if let Some(value) = first.strip_prefix("enable=") {
52                enable = Some(value.to_string());
53            } else if first.contains('=') {
54                let (key, value) = first.split_once('=').ok_or_else(|| format!("invalid -trace option: {first}"))?;
55                match key {
56                    "events" => events = Some(PathBuf::from(value)),
57                    "file" => file = Some(PathBuf::from(value)),
58                    other => return Err(format!("unsupported -trace option: {other}")),
59                }
60            } else {
61                enable = Some(first.to_string());
62            }
63        }
64
65        for part in parts {
66            if !part.contains('=') {
67                if enable.is_none() {
68                    enable = Some(part.to_string());
69                    continue;
70                }
71                return Err(format!("invalid -trace option: {part}"));
72            }
73
74            let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid -trace option: {part}"))?;
75            match key {
76                "enable" => enable = Some(value.to_string()),
77                "events" => events = Some(PathBuf::from(value)),
78                "file" => file = Some(PathBuf::from(value)),
79                other => return Err(format!("unsupported -trace option: {other}")),
80            }
81        }
82
83        Ok(Self { enable, events, file })
84    }
85}