use std::fmt;
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{Unexpected, Visitor},
};
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[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 ArgbColor(pub [u8; 4]);
struct ArgbVisitor;
impl<'de> Visitor<'de> for ArgbVisitor {
type Value = ArgbColor;
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(ArgbColor([255, r, g, b]))
} else if let Some(v) = v.strip_prefix("rgba:") {
let r = u32::from_str_radix(v.get(0..2)?, 16).ok()?;
let g = u32::from_str_radix(v.get(2..4)?, 16).ok()?;
let b = u32::from_str_radix(v.get(4..6)?, 16).ok()?;
let a = u32::from_str_radix(v.get(6..8)?, 16).ok()?;
Some(ArgbColor([
a as u8,
((r * a) >> 8) as u8,
((g * a) >> 8) as u8,
((b * a) >> 8) as u8,
]))
} else {
None
}
})()
.ok_or(E::invalid_value(
Unexpected::Other("Invalid color format"),
&"",
))
}
}
impl<'de> Deserialize<'de> for ArgbColor {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_string(ArgbVisitor)
}
}
impl Serialize for ArgbColor {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&format!(
"rgba:{}{}{}{}",
self.0[1], self.0[2], self.0[3], self.0[0]
))
}
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Color {
BuiltinColor(BuiltinColor),
Rgb(ArgbColor),
}
impl Color {
pub fn to_rgba(&self, config: &Config) -> Option<[u8; 4]> {
match self {
Color::BuiltinColor(color) => match color {
BuiltinColor::Black => Some(config.black.0),
BuiltinColor::Red => Some(config.red.0),
BuiltinColor::Green => Some(config.green.0),
BuiltinColor::Yellow => Some(config.yellow.0),
BuiltinColor::Blue => Some(config.blue.0),
BuiltinColor::Magenta => Some(config.magenta.0),
BuiltinColor::Cyan => Some(config.cyan.0),
BuiltinColor::White => Some(config.white.0),
BuiltinColor::BrightBlack => Some(config.bright_black.0),
BuiltinColor::BrightRed => Some(config.bright_red.0),
BuiltinColor::BrightGreen => Some(config.bright_green.0),
BuiltinColor::BrightYellow => Some(config.bright_yellow.0),
BuiltinColor::BrightBlue => Some(config.bright_blue.0),
BuiltinColor::BrightMagenta => Some(config.bright_magenta.0),
BuiltinColor::BrightCyan => Some(config.bright_cyan.0),
BuiltinColor::BrightWhite => Some(config.bright_white.0),
BuiltinColor::Default => None,
},
Color::Rgb(ArgbColor(c)) => Some(*c),
}
}
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Attribute {
Underline,
Reverse,
Blink,
Bold,
Dim,
Italic,
Strikethrough,
FinalFg,
FinalBg,
FinalAttr,
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Deserialize, Serialize)]
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, Serialize)]
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,
}
fn default_font() -> Vec<String> {
vec![String::from("monospace")]
}
fn default_font_size() -> u32 {
14
}
fn black() -> ArgbColor {
ArgbColor([255, 0, 0, 0])
}
fn red() -> ArgbColor {
ArgbColor([255, 255, 0, 0])
}
fn green() -> ArgbColor {
ArgbColor([255, 0, 255, 0])
}
fn yellow() -> ArgbColor {
ArgbColor([255, 255, 255, 0])
}
fn blue() -> ArgbColor {
ArgbColor([255, 0, 0, 255])
}
fn magenta() -> ArgbColor {
ArgbColor([255, 255, 0, 255])
}
fn cyan() -> ArgbColor {
ArgbColor([255, 0, 255, 255])
}
fn white() -> ArgbColor {
ArgbColor([255, 255, 255, 255])
}
fn bright_black() -> ArgbColor {
ArgbColor([255, 0, 0, 0])
}
fn bright_red() -> ArgbColor {
ArgbColor([255, 255, 0, 0])
}
fn bright_green() -> ArgbColor {
ArgbColor([255, 0, 255, 0])
}
fn bright_yellow() -> ArgbColor {
ArgbColor([255, 255, 255, 0])
}
fn bright_blue() -> ArgbColor {
ArgbColor([255, 0, 0, 255])
}
fn bright_magenta() -> ArgbColor {
ArgbColor([255, 255, 0, 255])
}
fn bright_cyan() -> ArgbColor {
ArgbColor([255, 0, 255, 255])
}
fn bright_white() -> ArgbColor {
ArgbColor([255, 255, 255, 255])
}
struct CommaListVisitor;
impl<'de> Visitor<'de> for CommaListVisitor {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("A list of comma seperated fonts")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v.split(',').map(String::from).collect())
}
}
fn comma_list_deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_string(CommaListVisitor)
}
struct StringIntegerVisitor;
impl<'de> Visitor<'de> for StringIntegerVisitor {
type Value = u32;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("An integer")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
v.parse()
.map_err(|_| E::invalid_value(Unexpected::Other("Expected string"), &""))
}
}
fn string_integer<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_string(StringIntegerVisitor)
}
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct Config {
#[serde(
default = "default_font",
deserialize_with = "comma_list_deserialize",
rename = "mosham_fonts"
)]
pub fonts: Vec<String>,
#[serde(
default = "default_font_size",
deserialize_with = "string_integer",
rename = "mosham_font_size"
)]
pub font_size: u32,
#[serde(default = "black", rename = "mosham_black")]
pub black: ArgbColor,
#[serde(default = "red", rename = "mosham_red")]
pub red: ArgbColor,
#[serde(default = "green", rename = "mosham_green")]
pub green: ArgbColor,
#[serde(default = "yellow", rename = "mosham_yellow")]
pub yellow: ArgbColor,
#[serde(default = "blue", rename = "mosham_blue")]
pub blue: ArgbColor,
#[serde(default = "magenta", rename = "mosham_magenta")]
pub magenta: ArgbColor,
#[serde(default = "cyan", rename = "mosham_cyan")]
pub cyan: ArgbColor,
#[serde(default = "white", rename = "mosham_white")]
pub white: ArgbColor,
#[serde(default = "bright_black", rename = "mosham_bright_black")]
pub bright_black: ArgbColor,
#[serde(default = "bright_red", rename = "mosham_bright_red")]
pub bright_red: ArgbColor,
#[serde(default = "bright_green", rename = "mosham_bright_green")]
pub bright_green: ArgbColor,
#[serde(default = "bright_yellow", rename = "mosham_bright_yellow")]
pub bright_yellow: ArgbColor,
#[serde(default = "bright_blue", rename = "mosham_bright_blue")]
pub bright_blue: ArgbColor,
#[serde(default = "bright_magenta", rename = "mosham_bright_magenta")]
pub bright_magenta: ArgbColor,
#[serde(default = "bright_cyan", rename = "mosham_bright_cyan")]
pub bright_cyan: ArgbColor,
#[serde(default = "bright_white", rename = "mosham_bright_white")]
pub bright_white: ArgbColor,
}
impl Default for Config {
fn default() -> Self {
Self {
fonts: default_font(),
font_size: default_font_size(),
black: black(),
red: red(),
green: green(),
yellow: yellow(),
blue: blue(),
magenta: magenta(),
cyan: cyan(),
white: white(),
bright_black: bright_black(),
bright_red: bright_red(),
bright_green: bright_green(),
bright_yellow: bright_yellow(),
bright_blue: bright_blue(),
bright_magenta: bright_magenta(),
bright_cyan: bright_cyan(),
bright_white: bright_white(),
}
}
}
#[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([Config; 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,
}
}
}