1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use std::ffi::CStr;
use crate::{error::*, ffi, global::GlobalInstance};
pub mod commands;
pub mod response;
#[cfg(feature = "global_singleton")]
pub mod global;
pub mod unique;
pub trait Command<'a> {
type Response;
const COMMAND_STR: &'static str;
fn parse_response(response: &'a str) -> Result<Self::Response, GencmdCmdError>;
}
#[derive(Clone)]
pub struct Gencmd {
buffer: [u8; ffi::GENCMDSERVICE_MSGFIFO_SIZE as usize]
}
impl Gencmd {
pub fn new() -> Self {
Gencmd {
buffer: [0u8; ffi::GENCMDSERVICE_MSGFIFO_SIZE as usize]
}
}
pub fn send_cmd_raw(
&mut self,
instance: &mut GlobalInstance,
command: &str
) -> Result<&str, GencmdCmdError> {
if command.len() >= ffi::GENCMD_MAX_LENGTH as usize {
return Err(GencmdCmdError::CommandTooLong)
}
debug_assert!(ffi::GENCMD_MAX_LENGTH <= ffi::GENCMDSERVICE_MSGFIFO_SIZE);
self.buffer[.. command.len()].copy_from_slice(command.as_bytes());
self.buffer[command.len()] = 0;
unsafe {
instance.send_command(
CStr::from_bytes_with_nul(&self.buffer[..= command.len()]).unwrap()
)?;
}
let len = instance.retrieve_response(&mut self.buffer)?;
let response = std::str::from_utf8(&self.buffer[.. len])?;
if response.starts_with("error=") {
let error = Self::parse_error(response)?;
return Err(error.into())
}
Ok(response)
}
pub fn send_cmd<'a, C: Command<'a>>(
&'a mut self,
instance: &mut GlobalInstance
) -> Result<C::Response, GencmdCmdError> {
let response = self.send_cmd_raw(instance, C::COMMAND_STR)?;
C::parse_response(response)
}
fn parse_error(response: &str) -> Result<GencmdErrorResponse, GencmdCmdError> {
let (response, code) = response::parse_field_simple::<i32>(response, "error")
.map_err(GencmdCmdError::from_invalid_format)?;
let (_, message) = response::parse_field_simple::<&str>(response, "error_msg")
.map_err(GencmdCmdError::from_invalid_format)?;
log::error!("gencmd returned: code: {} message: {}", code, message);
let error = match code {
1 => GencmdErrorResponse::CommandNotRegistered,
2 => GencmdErrorResponse::InvalidArguments,
_ => {
return Err(GencmdCmdError::InvalidResponseFormat(
"Invalid code".to_string().into()
))
}
};
Ok(error)
}
}