1use std::io;
2
3#[derive(Clone, Debug, Default)]
7pub struct Response {
8 pub control: Vec<(char, String)>,
10
11 pub message: Option<String>,
13}
14
15impl Response {
16 pub fn get_code(&self, code: char) -> Option<&str> {
18 for (key, value) in &self.control {
19 if *key == code {
20 return Some(value);
21 }
22 }
23 None
24 }
25
26 pub fn get_code_character(&self, code: char) -> Option<char> {
28 self.get_code(code).and_then(|value| value.chars().next())
29 }
30
31 pub fn get_code_integer(&self, code: char) -> Option<i32> {
33 self.get_code(code).and_then(|value| value.parse().ok())
34 }
35
36 pub fn is_ok(&self) -> bool {
38 self.message.as_ref().map(|message| message == "OK").unwrap_or_default()
39 }
40}
41
42impl TryFrom<&[u8]> for Response {
43 type Error = io::Error;
44
45 fn try_from(response: &[u8]) -> Result<Self, Self::Error> {
46 let response = str::from_utf8(response).map_err(io::Error::other)?;
47 if !response.is_ascii() {
48 return Err(io::Error::other("Kitty response is not ASCII"));
49 }
50
51 let (control, message) = match response.split_once(';') {
52 Some((control, message)) => (control, Some(message.into())),
53 None => (response, None),
54 };
55
56 let control: Vec<_> = control
58 .split(',')
59 .into_iter()
60 .filter_map(|control| control.split_once('='))
61 .filter_map(|(key, value)| key.chars().next().map(|key| (key, String::from(value))))
62 .collect();
63
64 Ok(Self { control, message })
65 }
66}