use crate::color::Color; use crate::pointer_btn::PointerBtn; use crate::text::Align; use crate::utils::{de_first_json, de_last_json, last_line}; use serde::{de, Deserialize, Serialize}; use std::io::{self, Error, ErrorKind}; #[derive(Clone, Deserialize, Default, Debug)] pub struct Block { pub full_text: String, #[serde(default)] pub short_text: Option, #[serde(default)] pub color: Option, #[serde(default)] pub background: Option, #[serde(default)] pub min_width: Option, #[serde(default)] pub align: Align, #[serde(default)] pub name: Option, #[serde(default)] pub instance: Option, #[serde(default = "def_sep")] pub separator: bool, #[serde(default = "def_sep_width")] pub separator_block_width: u8, #[serde(default)] pub markup: Option, } fn def_sep() -> bool { true } fn def_sep_width() -> u8 { 9 } #[derive(Clone, Debug, PartialEq, Eq)] pub enum MinWidth { Text(String), Pixels(u64), } #[derive(Serialize, Default)] pub struct Event<'a> { pub name: Option<&'a str>, pub instance: Option<&'a str>, pub button: PointerBtn, // Not available on wayland pub modifiers: Vec<()>, // I see no reason to have these in the protocol, as a lot depends on font & pango markup pub x: u8, pub y: u8, pub relative_x: u8, pub relative_y: u8, pub output_x: u8, pub output_y: u8, pub width: u8, pub height: u8, } #[derive(Deserialize, Clone, Copy, Debug)] #[serde(deny_unknown_fields)] pub struct JsonHeader { version: u8, #[serde(default)] #[allow(dead_code)] stop_signal: i32, #[serde(default)] #[allow(dead_code)] cont_signal: i32, #[serde(default)] click_events: bool, } #[derive(Debug)] pub enum Protocol { Unknown, PlainText { pending_line: Option, }, JsonNotStarted { header: JsonHeader, }, Json { header: JsonHeader, pending_blocks: Option>, }, } impl Protocol { /// Extract new data from `bytes`, return unused bytes. pub fn process_new_bytes<'a>(&mut self, bytes: &'a [u8]) -> io::Result<&'a [u8]> { match self { Self::Unknown => match de_first_json::(bytes) { Ok((Some(header), rem)) if header.version == 1 => { *self = Self::JsonNotStarted { header }; self.process_new_bytes(rem) } Ok((Some(header), _)) => Err(Error::new( ErrorKind::Other, format!("Protocol version {} is not supported", header.version), )), _ => { *self = Self::PlainText { pending_line: None }; self.process_new_bytes(bytes) } }, Self::PlainText { pending_line } => match last_line(bytes) { Some((new_line, rem)) => { *pending_line = Some(String::from_utf8_lossy(new_line).into()); Ok(rem) } None => Ok(bytes), }, Self::JsonNotStarted { header } => match bytes.trim_ascii_start() { [] => Ok(&[]), [b'[', rem @ ..] => { *self = Self::Json { header: *header, pending_blocks: None, }; self.process_new_bytes(rem) } [other, ..] => Err(Error::new( ErrorKind::InvalidData, format!("invalid json: expected '[', got '{}'", *other as char), )), }, Self::Json { pending_blocks: blocks, .. } => match de_last_json(bytes) { Err(e) => Err(Error::new( ErrorKind::InvalidData, format!("invalid json: {e}"), )), Ok((new_blocks, rem)) => { if let Some(new_blocks) = new_blocks { *blocks = Some(new_blocks); } Ok(rem) } }, } } pub fn get_blocks(&mut self) -> Option> { match self { Self::Unknown | Self::JsonNotStarted { .. } => None, Self::PlainText { pending_line, .. } => Some(vec![Block { full_text: pending_line.take()?, ..Default::default() }]), Self::Json { pending_blocks, .. } => pending_blocks.take(), } } pub fn supports_clicks(&self) -> bool { match self { Self::JsonNotStarted { header } | Self::Json { header, .. } => header.click_events, _ => false, } } } impl<'de> Deserialize<'de> for MinWidth { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct MinWidthVisitor; impl de::Visitor<'_> for MinWidthVisitor { type Value = MinWidth; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("positive integer or string") } fn visit_str(self, v: &str) -> Result where E: de::Error, { Ok(MinWidth::Text(v.to_owned())) } fn visit_string(self, v: String) -> Result where E: de::Error, { Ok(MinWidth::Text(v)) } fn visit_u64(self, v: u64) -> Result where E: de::Error, { Ok(MinWidth::Pixels(v)) } fn visit_i64(self, v: i64) -> Result where E: de::Error, { Ok(MinWidth::Pixels( v.try_into().map_err(|_| E::custom("invalid min_width"))?, )) } } deserializer.deserialize_any(MinWidthVisitor) } }