use std::{collections::HashMap, fmt};
use serde::{
Deserialize, Deserializer, Serialize,
de::{Unexpected, Visitor},
};
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BuiltinColor {
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
BrightBlack,
BrightRed,
BrightGreen,
BrightYellow,
BrightBlue,
BrightMagenta,
BrightCyan,
BrightWhite,
Default,
}
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub struct RgbaColor([u8; 4]);
struct RgbaVisitor;
impl<'de> Visitor<'de> for RgbaVisitor {
type Value = RgbaColor;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("A color in the format rgb:RRGGBB or rgba:RRGGBBAA")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
(|| {
if let Some(v) = v.strip_prefix("rgb:") {
let r = u8::from_str_radix(v.get(0..2)?, 16).ok()?;
let g = u8::from_str_radix(v.get(2..4)?, 16).ok()?;
let b = u8::from_str_radix(v.get(4..6)?, 16).ok()?;
Some(RgbaColor([r, g, b, 255]))
} else if let Some(v) = v.strip_prefix("rgba:") {
let r = u8::from_str_radix(v.get(0..2)?, 16).ok()?;
let g = u8::from_str_radix(v.get(2..4)?, 16).ok()?;
let b = u8::from_str_radix(v.get(4..6)?, 16).ok()?;
let a = u8::from_str_radix(v.get(6..8)?, 16).ok()?;
Some(RgbaColor([r, g, b, a]))
} else {
None
}
})()
.ok_or(E::invalid_value(
Unexpected::Other("Invalid color format"),
&"",
))
}
}
impl<'de> Deserialize<'de> for RgbaColor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_string(RgbaVisitor)
}
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize)]
#[serde(untagged)]
pub enum Color {
BuiltinColor(BuiltinColor),
Rgb(RgbaColor),
}
impl Color {
pub fn to_rgba(&self, default: [u8; 4]) -> [u8; 4] {
match self {
Color::BuiltinColor(color) => match color {
BuiltinColor::Black => [0, 0, 0, 255],
BuiltinColor::Red => [255, 0, 0, 255],
BuiltinColor::Green => [0, 255, 0, 255],
BuiltinColor::Yellow => [255, 255, 0, 255],
BuiltinColor::Blue => [0, 0, 255, 0],
BuiltinColor::Magenta => [255, 0, 255, 255],
BuiltinColor::Cyan => [0, 255, 255, 255],
BuiltinColor::White => [255, 255, 255, 255],
BuiltinColor::BrightBlack => [0, 0, 0, 255],
BuiltinColor::BrightRed => [255, 0, 0, 255],
BuiltinColor::BrightGreen => [0, 255, 0, 255],
BuiltinColor::BrightYellow => [255, 255, 0, 255],
BuiltinColor::BrightBlue => [0, 0, 255, 0],
BuiltinColor::BrightMagenta => [255, 0, 255, 255],
BuiltinColor::BrightCyan => [0, 255, 255, 255],
BuiltinColor::BrightWhite => [255, 255, 255, 255],
BuiltinColor::Default => default,
},
Color::Rgb(RgbaColor(c)) => *c,
}
}
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Attribute {
Underline,
Reverse,
Blink,
Bold,
Dim,
Italic,
FinalFg,
FinalBg,
FinalAttr,
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize)]
pub struct Face {
pub fg: Color,
pub bg: Color,
pub attributes: Vec<Attribute>,
}
impl Default for Face {
fn default() -> Self {
Self {
fg: Color::BuiltinColor(BuiltinColor::White),
bg: Color::BuiltinColor(BuiltinColor::Black),
attributes: Vec::new(),
}
}
}
#[derive(Debug, Hash, PartialEq, Eq, Deserialize)]
pub struct Atom {
pub face: Face,
pub contents: String,
}
pub type Line = Vec<Atom>;
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct Coord {
line: i32,
column: i32,
}
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum MenuShowStyle {
Prompt,
Search,
Inline,
}
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum InfoShowStyle {
Prompt,
Inline,
InlineAbove,
InlineBelow,
MenuDoc,
Modal,
}
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SetCursorMode {
Prompt,
Buffer,
}
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "snake_case", tag = "method", content = "params")]
pub enum Request {
Draw(Vec<Line>, Face, Face),
DrawStatus(Line, Line, Face),
MenuShow(Vec<Line>, Coord, Face, Face, MenuShowStyle),
MenuSelect([i32; 1]),
MenuHide([(); 0]),
InfoShow(Line, Vec<Line>, Coord, Face, InfoShowStyle),
InfoHide([(); 0]),
SetCursor(SetCursorMode, Coord),
SetUiOptions([HashMap<String, String>; 1]),
Refresh([bool; 1]),
}
#[derive(Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MouseEvent {
Move,
Press,
Release,
WheelUp,
WheelDown,
}
#[derive(Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case", tag = "method", content = "params")]
pub enum Response {
Keys(Vec<String>),
Resize(u32, u32),
Mouse(MouseEvent, i32, i32),
MenuSelect([i32; 0]),
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Rpc<T> {
pub jsonrpc: String,
#[serde(flatten)]
pub inner: T,
}
impl<T> From<T> for Rpc<T> {
fn from(inner: T) -> Self {
Rpc {
jsonrpc: String::from("2.0"),
inner,
}
}
}