Skip to main content

little_kitty/
response.rs

1use std::io;
2
3/// Kitty graphics protocol APC (Application Programming Command) response.
4///
5/// See [documentation](https://sw.kovidgoyal.net/kitty/graphics-protocol/).
6#[derive(Clone, Debug, Default)]
7pub struct Response {
8    /// Control codes.
9    pub control: Vec<(char, String)>,
10
11    /// Message ("OK" or error).
12    pub message: Option<String>,
13}
14
15impl Response {
16    /// Get the value of a code.
17    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    /// Get the value of a code as a character.
27    pub fn get_code_character(&self, code: char) -> Option<char> {
28        self.get_code(code).and_then(|value| value.chars().next())
29    }
30
31    /// Get the value of a code as an integer.
32    pub fn get_code_integer(&self, code: char) -> Option<i32> {
33        self.get_code(code).and_then(|value| value.parse().ok())
34    }
35
36    /// Whether the message is "OK".
37    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        // We will be forgiving and ignore malformed codes
57        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}