use std::collections::BTreeMap;
use crate::keybinding::KeyBindings;
#[derive(Debug)]
pub struct Config {
pub keybindings: KeyBindings,
pub preview: FrameSize,
pub palette: Palette,
}
impl<'text> nojson::FromRawJsonValue<'text> for Config {
fn from_raw_json_value(
value: nojson::RawJsonValue<'text, '_>,
) -> Result<Self, nojson::JsonParseError> {
let ([keybindings, preview, palette], []) =
value.to_fixed_object(["keybindings", "preview", "palette"], [])?;
Ok(Config {
keybindings: keybindings.try_to()?,
preview: preview.try_to()?, palette: palette.try_to()?,
})
}
}
impl Default for Config {
fn default() -> Self {
let json = include_str!("../default.config.json");
let nojson::Json(config) = json.parse().expect("bug");
config
}
}
#[derive(Debug)]
pub struct FrameSize {
pub width: usize,
pub height: usize,
}
impl std::str::FromStr for FrameSize {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (width, height) = s
.split_once('x')
.ok_or_else(|| format!("Invalid format: expected 'WIDTHxHEIGHT', got '{}'", s))?;
let width = width
.parse()
.map_err(|e| format!("Invalid width '{}': {}", width, e))?;
let height = height
.parse()
.map_err(|e| format!("Invalid height '{}': {}", height, e))?;
Ok(FrameSize { width, height })
}
}
impl<'text> nojson::FromRawJsonValue<'text> for FrameSize {
fn from_raw_json_value(
value: nojson::RawJsonValue<'text, '_>,
) -> Result<Self, nojson::JsonParseError> {
let ([width, height], []) = value.to_fixed_object(["width", "height"], [])?;
Ok(FrameSize {
width: width.try_to()?,
height: height.try_to()?,
})
}
}
#[derive(Debug)]
pub struct Palette {
pub colors: BTreeMap<char, Color>,
}
impl<'text> nojson::FromRawJsonValue<'text> for Palette {
fn from_raw_json_value(
value: nojson::RawJsonValue<'text, '_>,
) -> Result<Self, nojson::JsonParseError> {
let colors = value.try_to()?;
Ok(Palette { colors })
}
}
#[derive(Debug, Clone, Copy)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self::rgba(r, g, b, 255)
}
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
}
impl<'text> nojson::FromRawJsonValue<'text> for Color {
fn from_raw_json_value(
value: nojson::RawJsonValue<'text, '_>,
) -> Result<Self, nojson::JsonParseError> {
let hex_string = value.to_unquoted_string_str()?;
let hex = hex_string
.strip_prefix('#')
.ok_or_else(|| value.invalid("Color must start with #"))?;
let (r, g, b, a) = match hex.len() {
6 => {
let r = u8::from_str_radix(&hex[0..2], 16)
.map_err(|_| value.invalid("Invalid hex color format"))?;
let g = u8::from_str_radix(&hex[2..4], 16)
.map_err(|_| value.invalid("Invalid hex color format"))?;
let b = u8::from_str_radix(&hex[4..6], 16)
.map_err(|_| value.invalid("Invalid hex color format"))?;
(r, g, b, 255)
}
8 => {
let r = u8::from_str_radix(&hex[0..2], 16)
.map_err(|_| value.invalid("Invalid hex color format"))?;
let g = u8::from_str_radix(&hex[2..4], 16)
.map_err(|_| value.invalid("Invalid hex color format"))?;
let b = u8::from_str_radix(&hex[4..6], 16)
.map_err(|_| value.invalid("Invalid hex color format"))?;
let a = u8::from_str_radix(&hex[6..8], 16)
.map_err(|_| value.invalid("Invalid hex color format"))?;
(r, g, b, a)
}
_ => {
return Err(value.invalid("Color must be 6 or 8 hex digits"));
}
};
Ok(Color::rgba(r, g, b, a))
}
}