use crate::message::{Message, Params};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ErrorCode {
NotSupported,
Invalid,
Protocol,
NotFound,
NoMemory,
Permission,
Version,
Other(String),
}
impl ErrorCode {
pub fn as_str(&self) -> &str {
match self {
ErrorCode::NotSupported => "ENOTSUP",
ErrorCode::Invalid => "EINVAL",
ErrorCode::Protocol => "EPROTO",
ErrorCode::NotFound => "ENOENT",
ErrorCode::NoMemory => "ENOMEM",
ErrorCode::Permission => "EPERM",
ErrorCode::Version => "EVERSION",
ErrorCode::Other(s) => s,
}
}
pub fn from_code(s: &str) -> Self {
match s {
"ENOTSUP" => ErrorCode::NotSupported,
"EINVAL" => ErrorCode::Invalid,
"EPROTO" => ErrorCode::Protocol,
"ENOENT" => ErrorCode::NotFound,
"ENOMEM" => ErrorCode::NoMemory,
"EPERM" => ErrorCode::Permission,
"EVERSION" => ErrorCode::Version,
o => ErrorCode::Other(o.to_string()),
}
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Reply {
pub subsystem: u8,
pub id: u32,
pub params: Params,
}
impl Reply {
pub fn ok(subsystem: u8, id: u32, extra: Params) -> Self {
let mut params = Params::new().with("ok", 1);
for (k, v) in extra.iter() {
params.push(k, v);
}
Reply { subsystem, id, params }
}
pub fn raw(subsystem: u8, id: u32, params: Params) -> Self {
Reply { subsystem, id, params }
}
pub fn error(subsystem: u8, id: u32, code: ErrorCode, msg: &str) -> Self {
Reply { subsystem, id, params: Params::new().with("ok", 0).with("err", code.as_str()).with("msg", msg) }
}
pub fn is_ok(&self) -> bool {
self.params.get("ok") != Some("0")
}
pub fn error_code(&self) -> Option<ErrorCode> {
(!self.is_ok()).then(|| ErrorCode::from_code(self.params.get("err").unwrap_or("")))
}
pub fn msg(&self) -> Option<&str> {
self.params.get("msg")
}
pub fn from_message(m: &Message) -> Option<Self> {
if m.verb != b'r' {
return None;
}
let id = m.params.num("id").unwrap_or(0);
let params = m.params.iter().filter(|(k, _)| *k != "id").collect();
Some(Reply { subsystem: m.subsystem, id, params })
}
pub fn to_message(&self) -> Message {
let mut p = Params::new().with("id", self.id);
for (k, v) in self.params.iter() {
p.push(k, v);
}
Message::new(self.subsystem, b'r', p)
}
pub fn encode(&self) -> Vec<u8> {
self.to_message().encode()
}
}
pub fn capability_query(id: u32) -> Vec<u8> {
Message::new(b'c', b'q', Params::new().with("id", id)).encode()
}
pub fn detect_query(id: u32) -> Vec<u8> {
let mut v = capability_query(id);
v.extend_from_slice(b"\x1b[c");
v
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Capabilities {
pub id: u32,
pub version: u32,
pub subsystems: String,
pub raw: bool,
pub max_chunk: Option<u32>,
pub params: Params,
}
impl Capabilities {
pub fn from_message(m: &Message) -> Option<Self> {
if !m.is(b'c', b'r') {
return None;
}
let p = &m.params;
Some(Self::new(p.num("id").unwrap_or(0), p.iter().filter(|(k, _)| *k != "id").collect()))
}
pub fn new(id: u32, params: Params) -> Self {
let p = ¶ms;
Capabilities {
id,
version: p.num("v").unwrap_or(0),
subsystems: p.get("sub").unwrap_or("").to_string(),
raw: p.get("raw") == Some("1"),
max_chunk: p.num("max_chunk"),
params: params.clone(),
}
}
pub fn has_subsystem(&self, s: char) -> bool {
self.subsystems.contains(s)
}
pub fn keyboard(&self) -> bool {
self.has_subsystem('k') && self.params.get("k") == Some("1")
}
pub fn encode(&self) -> Vec<u8> {
Reply::raw(b'c', self.id, self.params.clone()).encode()
}
}