use serde::Deserialize;
use std::collections::HashMap;
use std::path::Path;
#[derive(Debug, Clone, Deserialize)]
pub struct TableChars {
pub tl: String,
pub tr: String,
pub bl: String,
pub br: String,
pub h: String,
pub v: String,
pub tm: String,
pub bm: String,
pub lm: String,
pub rm: String,
pub x: String,
}
impl Default for TableChars {
fn default() -> Self {
Self {
tl: "\u{250c}".into(),
tr: "\u{2510}".into(),
bl: "\u{2514}".into(),
br: "\u{2518}".into(),
h: "\u{2500}".into(),
v: "\u{2502}".into(),
tm: "\u{252c}".into(),
bm: "\u{2534}".into(),
lm: "\u{251c}".into(),
rm: "\u{2524}".into(),
x: "\u{253c}".into(),
}
}
}
pub fn default_syntax() -> HashMap<String, String> {
let mut m = HashMap::new();
m.insert("comment".into(), "\x1b[3;90m".into());
m.insert("error".into(), "\x1b[1;31m".into());
m.insert("keyword".into(), "\x1b[35m".into());
m.insert("keyword_constant".into(), "\x1b[36m".into());
m.insert("keyword_declaration".into(), "\x1b[35m".into());
m.insert("keyword_namespace".into(), "\x1b[35m".into());
m.insert("keyword_pseudo".into(), "\x1b[35m".into());
m.insert("keyword_reserved".into(), "\x1b[35m".into());
m.insert("keyword_type".into(), "\x1b[36m".into());
m.insert("name_attribute".into(), "\x1b[33m".into());
m.insert("name_builtin".into(), "\x1b[36m".into());
m.insert("name_builtin_pseudo".into(), "\x1b[36m".into());
m.insert("name_class".into(), "\x1b[1;36m".into());
m.insert("name_constant".into(), "\x1b[1;33m".into());
m.insert("name_decorator".into(), "\x1b[33m".into());
m.insert("name_entity".into(), "\x1b[1;33m".into());
m.insert("name_exception".into(), "\x1b[1;31m".into());
m.insert("name_function".into(), "\x1b[33m".into());
m.insert("name_function_magic".into(), "\x1b[33m".into());
m.insert("name_label".into(), "\x1b[36m".into());
m.insert("name_namespace".into(), "\x1b[1;36m".into());
m.insert("name_tag".into(), "\x1b[1;35m".into());
m.insert("name_variable".into(), "\x1b[37m".into());
m.insert("string".into(), "\x1b[32m".into());
m.insert("string_char".into(), "\x1b[32m".into());
m.insert("string_delimiter".into(), "\x1b[32m".into());
m.insert("string_doc".into(), "\x1b[3;32m".into());
m.insert("string_escape".into(), "\x1b[1;32m".into());
m.insert("string_interpol".into(), "\x1b[1;32m".into());
m.insert("string_regex".into(), "\x1b[31m".into());
m.insert("string_sigil".into(), "\x1b[32m".into());
m.insert("string_symbol".into(), "\x1b[36m".into());
m.insert("number".into(), "\x1b[34m".into());
m.insert("operator".into(), String::new());
m.insert("operator_word".into(), "\x1b[35m".into());
m.insert("punctuation".into(), String::new());
m.insert("storage".into(), "\x1b[35m".into());
m.insert("storage_type".into(), "\x1b[35m".into());
m.insert("storage_modifier".into(), "\x1b[35m".into());
m.insert("entity".into(), "\x1b[33m".into());
m.insert("entity_name".into(), "\x1b[33m".into());
m.insert("entity_other".into(), "\x1b[33m".into());
m.insert("support".into(), "\x1b[36m".into());
m.insert("support_macro".into(), "\x1b[1;36m".into());
m.insert("support_function".into(), "\x1b[36m".into());
m.insert("support_type".into(), "\x1b[36m".into());
m.insert("support_constant".into(), "\x1b[36m".into());
m.insert("constant".into(), "\x1b[34m".into());
m.insert("constant_numeric".into(), "\x1b[34m".into());
m.insert("constant_language".into(), "\x1b[36m".into());
m.insert("constant_character".into(), "\x1b[32m".into());
m.insert("constant_other".into(), "\x1b[1;34m".into());
m.insert("variable".into(), "\x1b[37m".into());
m.insert("variable_other".into(), "\x1b[37m".into());
m.insert("variable_parameter".into(), "\x1b[37m".into());
m.insert("generic_deleted".into(), "\x1b[31m".into());
m.insert("generic_emph".into(), "\x1b[3m".into());
m.insert("generic_error".into(), "\x1b[31m".into());
m.insert("generic_heading".into(), "\x1b[1m".into());
m.insert("generic_inserted".into(), "\x1b[32m".into());
m.insert("generic_output".into(), "\x1b[90m".into());
m.insert("generic_prompt".into(), "\x1b[1m".into());
m.insert("generic_strong".into(), "\x1b[1m".into());
m.insert("generic_subheading".into(), "\x1b[1;35m".into());
m.insert("generic_traceback".into(), "\x1b[31m".into());
m
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct Theme {
pub reset: String,
pub h1: String,
pub h2: String,
pub h3: String,
pub bold: String,
pub italic: String,
pub strikethrough: String,
pub inline_code: String,
pub link_text: String,
pub link_url: String,
pub image_text: String,
pub image_prefix: String,
pub image_suffix: String,
pub image_url: String,
pub code_border: String,
pub code_text: String,
pub code_top: String,
pub code_left: String,
pub code_bottom: String,
pub block_quote: String,
pub block_quote_prefix: String,
pub bullet_marker: String,
pub task_checked: String,
pub task_unchecked: String,
pub ordered_indent: String,
pub ordered_glyphs: Vec<String>,
pub list_continuation: String,
pub thematic_break: String,
pub thematic_break_char: String,
pub thematic_break_width: usize,
pub html_block: String,
pub table_border: String,
pub table_header: String,
pub table_chars: TableChars,
pub syntax_highlight: bool,
pub syntax: HashMap<String, String>,
}
impl Default for Theme {
fn default() -> Self {
Self {
reset: "\x1b[0m".into(),
h1: "\x1b[1;33m".into(),
h2: "\x1b[1;36m".into(),
h3: "\x1b[1;37m".into(),
bold: "\x1b[1m".into(),
italic: "\x1b[3m".into(),
strikethrough: "\x1b[9m".into(),
inline_code: "\x1b[32m".into(),
link_text: "\x1b[4m\x1b[34m".into(),
link_url: "\x1b[2m".into(),
image_text: "\x1b[2m".into(),
image_prefix: "[image: ".into(),
image_suffix: "]".into(),
image_url: "\x1b[2m".into(),
code_border: "\x1b[2m".into(),
code_text: "\x1b[32m".into(),
code_top: " \u{250c}\u{2500}".into(),
code_left: " \u{2502} ".into(),
code_bottom: " \u{2514}\u{2500}".into(),
block_quote: "\x1b[2m".into(),
block_quote_prefix: " \u{2502} ".into(),
bullet_marker: " \u{25b8} ".into(),
task_checked: " \u{2611} ".into(),
task_unchecked: " \u{2610} ".into(),
ordered_indent: " ".into(),
ordered_glyphs: vec![
"\u{2460}".into(),
"\u{2461}".into(),
"\u{2462}".into(),
"\u{2463}".into(),
"\u{2464}".into(),
"\u{2465}".into(),
"\u{2466}".into(),
"\u{2467}".into(),
"\u{2468}".into(),
"\u{2469}".into(),
"\u{246a}".into(),
"\u{246b}".into(),
"\u{246c}".into(),
"\u{246d}".into(),
"\u{246e}".into(),
"\u{246f}".into(),
"\u{2470}".into(),
"\u{2471}".into(),
"\u{2472}".into(),
"\u{2473}".into(),
],
list_continuation: " ".into(),
thematic_break: "\x1b[2m".into(),
thematic_break_char: "\u{2500}".into(),
thematic_break_width: 40,
html_block: "\x1b[2m".into(),
table_border: "\x1b[2m".into(),
table_header: "\x1b[1m".into(),
table_chars: TableChars::default(),
syntax_highlight: true,
syntax: default_syntax(),
}
}
}
impl Theme {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
let path = path.as_ref();
if !path.exists() {
return Ok(Self::default());
}
let contents = std::fs::read_to_string(path)?;
let theme: Theme = toml::from_str(&contents)?;
Ok(theme)
}
}