little-kitty 0.0.3

A low-level interface for the Kitty Graphics Protocol
Documentation
use std::io::*;

/// Kitty graphics protocol APC (Application Programming Command) response.
///
/// See [documentation](https://sw.kovidgoyal.net/kitty/graphics-protocol/).
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct Response {
    /// Control codes.
    pub control: Vec<(char, String)>,

    /// Message ("OK" or error).
    pub message: Option<String>,
}

impl Response {
    /// Get the value of a code.
    pub fn get_code(&self, code: char) -> Option<&str> {
        for (key, value) in &self.control {
            if *key == code {
                return Some(value);
            }
        }
        None
    }

    /// Get the value of a code as a character.
    pub fn get_code_character(&self, code: char) -> Option<char> {
        self.get_code(code).and_then(|value| value.chars().next())
    }

    /// Get the value of a code as an integer.
    pub fn get_code_integer(&self, code: char) -> Option<i32> {
        self.get_code(code).and_then(|value| value.parse().ok())
    }

    /// Whether the message is "OK".
    pub fn is_ok(&self) -> bool {
        self.message.as_ref().map(|message| message == "OK").unwrap_or_default()
    }
}

impl TryFrom<&[u8]> for Response {
    type Error = Error;

    fn try_from(response: &[u8]) -> Result<Self> {
        let response = str::from_utf8(response).map_err(Error::other)?;
        if !response.is_ascii() {
            return Err(Error::other("Kitty response is not ASCII"));
        }

        let (control, message) = match response.split_once(';') {
            Some((control, message)) => (control, Some(message.into())),
            None => (response, None),
        };

        // We will be forgiving and ignore malformed codes
        let control: Vec<_> = control
            .split(',')
            .into_iter()
            .filter_map(|control| control.split_once('='))
            .filter_map(|(key, value)| key.chars().next().map(|key| (key, String::from(value))))
            .collect();

        Ok(Self { control, message })
    }
}