qtest/
lib.rs

1/// Parser module, interface to interact with qtest
2pub mod parser;
3/// Socket module, used to serve and manage qtest socket connections.
4pub mod socket;
5
6/// QTest Response enum
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub enum Response {
9    /// Successfull response, without any additional data
10    Ok,
11    /// Successfull response, with additional data
12    OkVal(String),
13    /// Error in processing the request
14    Err(String),
15}
16
17// Converts a qtest response string to a Response enum
18impl From<&str> for Response {
19    fn from(s: &str) -> Self {
20        let mut s_parts = s.split_whitespace();
21        if s_parts.next() != Some("OK") {
22            return Self::Err(s.to_string());
23        }
24        match s_parts.next() {
25            Some(val) => {
26                let msg = std::iter::once(val)
27                    .chain(s_parts)
28                    .collect::<Vec<_>>()
29                    .join(" ");
30                Self::OkVal(msg)
31            }
32            None => Self::Ok,
33        }
34    }
35}
36
37/// Struct for defining IRQ events propagated by QEMU.
38///
39/// The line and state depends on the machine that emits the event.
40/// Refer to QEMU documentation for your desired machine.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub struct Irq {
43    /// The line of the IRQ event
44    pub line: usize,
45    /// The state of the IRQ event
46    pub state: IrqState,
47}
48
49impl Irq {
50    /// Creates a new IRQ instance
51    pub fn new(line: usize, state: IrqState) -> Self {
52        Irq { line, state }
53    }
54}
55
56/// Enum for defining the state of an IRQ event
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum IrqState {
59    /// The IRQ event is raised
60    Raise,
61    /// The IRQ event is lowered
62    Lower,
63}
64
65impl TryFrom<&str> for Irq {
66    type Error = &'static str;
67
68    fn try_from(s: &str) -> Result<Self, Self::Error> {
69        let mut s_parts = s.split_whitespace();
70
71        if s_parts.next() != Some("IRQ") {
72            return Err("Invalid IRQ string");
73        }
74        let ty = match s_parts.next() {
75            Some("raise") => IrqState::Raise,
76            Some("lower") => IrqState::Lower,
77            _ => return Err("Invalid IRQ type"),
78        };
79        let line = s_parts
80            .next()
81            .ok_or("Invalid IRQ line")?
82            .parse()
83            .map_err(|_| "Invalid IRQ line")?;
84
85        match s_parts.next() {
86            Some(_) => Err("Invalid IRQ string"),
87            None => Ok(Irq::new(line, ty)),
88        }
89    }
90}
91
92#[cfg(test)]
93mod test {
94    use super::*;
95
96    #[test]
97    fn test_response_from() {
98        let response = Response::from("OK");
99        assert_eq!(response, Response::Ok);
100
101        let response = Response::from("OK val");
102        assert_eq!(response, Response::OkVal("val".to_string()));
103
104        let response = Response::from("ERR error");
105        assert_eq!(response, Response::Err("ERR error".to_string()));
106    }
107
108    #[test]
109    fn test_irq_try_from() {
110        let irq = Irq::try_from("invalid");
111        assert_eq!(irq, Err("Invalid IRQ string"));
112
113        let irq = Irq::try_from("IRQ invalid");
114        assert_eq!(irq, Err("Invalid IRQ type"));
115
116        let irq = Irq::try_from("IRQ raise -1");
117        assert_eq!(irq, Err("Invalid IRQ line"));
118
119        let irq = Irq::try_from("IRQ raise 1 invalid");
120        assert_eq!(irq, Err("Invalid IRQ string"));
121
122        let irq = Irq::try_from("IRQ raise 1");
123        assert_eq!(irq, Ok(Irq::new(1, IrqState::Raise)));
124
125        let irq = Irq::try_from("IRQ lower 2");
126        assert_eq!(irq, Ok(Irq::new(2, IrqState::Lower)));
127    }
128}