use std::str::FromStr;
use super::settings::{ParseSettings, Settings};
use super::style::*;
use super::selector::*;
use crate::parsing::ParseScopeError;
use self::ParseThemeError::*;
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Theme {
pub name: Option<String>,
pub author: Option<String>,
pub settings: ThemeSettings,
pub scopes: Vec<ThemeItem>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ThemeSettings {
pub foreground: Option<Color>,
pub background: Option<Color>,
pub caret: Option<Color>,
pub line_highlight: Option<Color>,
pub misspelling: Option<Color>,
pub minimap_border: Option<Color>,
pub accent: Option<Color>,
pub popup_css: Option<String>,
pub phantom_css: Option<String>,
pub bracket_contents_foreground: Option<Color>,
pub bracket_contents_options: Option<UnderlineOption>,
pub brackets_foreground: Option<Color>,
pub brackets_background: Option<Color>,
pub brackets_options: Option<UnderlineOption>,
pub tags_foreground: Option<Color>,
pub tags_options: Option<UnderlineOption>,
pub highlight: Option<Color>,
pub find_highlight: Option<Color>,
pub find_highlight_foreground: Option<Color>,
pub gutter: Option<Color>,
pub gutter_foreground: Option<Color>,
pub selection: Option<Color>,
pub selection_foreground: Option<Color>,
pub selection_background: Option<Color>,
pub selection_border: Option<Color>,
pub inactive_selection: Option<Color>,
pub inactive_selection_foreground: Option<Color>,
pub guide: Option<Color>,
pub active_guide: Option<Color>,
pub stack_guide: Option<Color>,
pub highlight_foreground: Option<Color>,
pub shadow: Option<Color>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ThemeItem {
pub scope: ScopeSelectors,
pub style: StyleModifier,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum UnderlineOption {
None,
Underline,
StippledUnderline,
SquigglyUnderline,
}
#[derive(Debug)]
pub enum ParseThemeError {
IncorrectUnderlineOption,
IncorrectFontStyle(String),
IncorrectColor,
IncorrectSyntax,
IncorrectSettings,
UndefinedSettings,
UndefinedScopeSettings(String),
ColorShemeScopeIsNotObject,
ColorShemeSettingsIsNotObject,
ScopeSelectorIsNotString(String),
DuplicateSettings,
ScopeParse(ParseScopeError),
}
impl From<ParseScopeError> for ParseThemeError {
fn from(error: ParseScopeError) -> ParseThemeError {
ScopeParse(error)
}
}
impl Default for UnderlineOption {
fn default() -> UnderlineOption {
UnderlineOption::None
}
}
impl Default for FontStyle {
fn default() -> FontStyle {
FontStyle::empty()
}
}
impl FromStr for UnderlineOption {
type Err = ParseThemeError;
fn from_str(s: &str) -> Result<UnderlineOption, Self::Err> {
Ok(match s {
"underline" => UnderlineOption::Underline,
"stippled_underline" => UnderlineOption::StippledUnderline,
"squiggly_underline" => UnderlineOption::SquigglyUnderline,
_ => return Err(IncorrectUnderlineOption),
})
}
}
impl ParseSettings for UnderlineOption {
type Error = ParseThemeError;
fn parse_settings(settings: Settings) -> Result<UnderlineOption, Self::Error> {
match settings {
Settings::String(value) => UnderlineOption::from_str(&value),
_ => Err(IncorrectUnderlineOption),
}
}
}
impl FromStr for FontStyle {
type Err = ParseThemeError;
fn from_str(s: &str) -> Result<FontStyle, Self::Err> {
let mut font_style = FontStyle::empty();
for i in s.split_whitespace() {
font_style.insert(match i {
"bold" => FontStyle::BOLD,
"underline" => FontStyle::UNDERLINE,
"italic" => FontStyle::ITALIC,
"normal" |
"regular" => FontStyle::empty(),
s => return Err(IncorrectFontStyle(s.to_owned())),
})
}
Ok(font_style)
}
}
impl ParseSettings for FontStyle {
type Error = ParseThemeError;
fn parse_settings(settings: Settings) -> Result<FontStyle, Self::Error> {
match settings {
Settings::String(value) => FontStyle::from_str(&value),
c => Err(IncorrectFontStyle(c.to_string())),
}
}
}
impl FromStr for Color {
type Err = ParseThemeError;
fn from_str(s: &str) -> Result<Color, Self::Err> {
let mut chars = s.chars();
if chars.next() != Some('#') {
return Err(IncorrectColor);
}
let mut d = Vec::new();
for char in chars {
d.push(char.to_digit(16).ok_or(IncorrectColor)? as u8);
}
Ok(match d.len() {
3 => {
Color {
r: d[0],
g: d[1],
b: d[2],
a: 255,
}
}
6 => {
Color {
r: d[0] * 16 + d[1],
g: d[2] * 16 + d[3],
b: d[4] * 16 + d[5],
a: 255,
}
}
8 => {
Color {
r: d[0] * 16 + d[1],
g: d[2] * 16 + d[3],
b: d[4] * 16 + d[5],
a: d[6] * 16 + d[7],
}
}
_ => return Err(IncorrectColor),
})
}
}
impl ParseSettings for Color {
type Error = ParseThemeError;
fn parse_settings(settings: Settings) -> Result<Color, Self::Error> {
match settings {
Settings::String(value) => Color::from_str(&value),
_ => Err(IncorrectColor),
}
}
}
impl ParseSettings for StyleModifier {
type Error = ParseThemeError;
fn parse_settings(settings: Settings) -> Result<StyleModifier, Self::Error> {
let mut obj = match settings {
Settings::Object(obj) => obj,
_ => return Err(ColorShemeScopeIsNotObject),
};
let font_style = match obj.remove("fontStyle") {
Some(Settings::String(value)) => Some(FontStyle::from_str(&value)?),
None => None,
Some(c) => return Err(IncorrectFontStyle(c.to_string())),
};
let foreground = match obj.remove("foreground") {
Some(Settings::String(value)) => Some(Color::from_str(&value)?),
None => None,
_ => return Err(IncorrectColor),
};
let background = match obj.remove("background") {
Some(Settings::String(value)) => Some(Color::from_str(&value)?),
None => None,
_ => return Err(IncorrectColor),
};
Ok(StyleModifier {
foreground,
background,
font_style,
})
}
}
impl ParseSettings for ThemeItem {
type Error = ParseThemeError;
fn parse_settings(settings: Settings) -> Result<ThemeItem, Self::Error> {
let mut obj = match settings {
Settings::Object(obj) => obj,
_ => return Err(ColorShemeScopeIsNotObject),
};
let scope = match obj.remove("scope") {
Some(Settings::String(value)) => ScopeSelectors::from_str(&value)?,
_ => return Err(ScopeSelectorIsNotString(format!("{:?}", obj))),
};
let style = match obj.remove("settings") {
Some(settings) => StyleModifier::parse_settings(settings)?,
None => return Err(IncorrectSettings),
};
Ok(ThemeItem {
scope,
style,
})
}
}
impl ParseSettings for ThemeSettings {
type Error = ParseThemeError;
fn parse_settings(json: Settings) -> Result<ThemeSettings, Self::Error> {
let mut settings = ThemeSettings::default();
let obj = match json {
Settings::Object(obj) => obj,
_ => return Err(ColorShemeSettingsIsNotObject),
};
for (key, value) in obj {
match &key[..] {
"foreground" => settings.foreground = Color::parse_settings(value).ok(),
"background" => settings.background = Color::parse_settings(value).ok(),
"caret" => settings.caret = Color::parse_settings(value).ok(),
"lineHighlight" => settings.line_highlight = Color::parse_settings(value).ok(),
"misspelling" => settings.misspelling = Color::parse_settings(value).ok(),
"minimapBorder" => settings.minimap_border = Color::parse_settings(value).ok(),
"accent" => settings.accent = Color::parse_settings(value).ok(),
"popupCss" => settings.popup_css = value.as_str().map(|s| s.to_owned()),
"phantomCss" => settings.phantom_css = value.as_str().map(|s| s.to_owned()),
"bracketContentsForeground" => {
settings.bracket_contents_foreground = Color::parse_settings(value).ok()
}
"bracketContentsOptions" => {
settings.bracket_contents_options = UnderlineOption::parse_settings(value).ok()
}
"bracketsForeground" => {
settings.brackets_foreground = Color::parse_settings(value).ok()
}
"bracketsBackground" => {
settings.brackets_background = Color::parse_settings(value).ok()
}
"bracketsOptions" => {
settings.brackets_options = UnderlineOption::parse_settings(value).ok()
}
"tagsForeground" => settings.tags_foreground = Color::parse_settings(value).ok(),
"tagsOptions" => {
settings.tags_options = UnderlineOption::parse_settings(value).ok()
}
"highlight" => settings.highlight = Color::parse_settings(value).ok(),
"findHighlight" => settings.find_highlight = Color::parse_settings(value).ok(),
"findHighlightForeground" => {
settings.find_highlight_foreground = Color::parse_settings(value).ok()
}
"gutter" => settings.gutter = Color::parse_settings(value).ok(),
"gutterForeground" => {
settings.gutter_foreground = Color::parse_settings(value).ok()
}
"selection" => settings.selection = Color::parse_settings(value).ok(),
"selectionForeground" => {
settings.selection_foreground = Color::parse_settings(value).ok()
}
"selectionBorder" => settings.selection_border = Color::parse_settings(value).ok(),
"inactiveSelection" => {
settings.inactive_selection = Color::parse_settings(value).ok()
}
"inactiveSelectionForeground" => {
settings.inactive_selection_foreground = Color::parse_settings(value).ok()
}
"guide" => settings.guide = Color::parse_settings(value).ok(),
"activeGuide" => settings.active_guide = Color::parse_settings(value).ok(),
"stackGuide" => settings.stack_guide = Color::parse_settings(value).ok(),
"shadow" => settings.shadow = Color::parse_settings(value).ok(),
"shadowWidth"|
"invisibles" |
_ => (),
}
}
Ok(settings)
}
}
impl ParseSettings for Theme {
type Error = ParseThemeError;
fn parse_settings(settings: Settings) -> Result<Theme, Self::Error> {
let mut obj = match settings {
Settings::Object(obj) => obj,
_ => return Err(IncorrectSyntax),
};
let name = match obj.remove("name") {
Some(Settings::String(name)) => Some(name),
None => None,
_ => return Err(IncorrectSyntax),
};
let author = match obj.remove("author") {
Some(Settings::String(author)) => Some(author),
None => None,
_ => return Err(IncorrectSyntax),
};
let items = match obj.remove("settings") {
Some(Settings::Array(items)) => items,
_ => return Err(IncorrectSyntax),
};
let mut iter = items.into_iter();
let settings = match iter.next() {
Some(Settings::Object(mut obj)) => {
match obj.remove("settings") {
Some(settings) => ThemeSettings::parse_settings(settings)?,
None => return Err(UndefinedSettings),
}
}
_ => return Err(UndefinedSettings),
};
let mut scopes = Vec::new();
for json in iter {
if let Ok(item) = ThemeItem::parse_settings(json) {
scopes.push(item);
}
}
Ok(Theme {
name,
author,
settings,
scopes,
})
}
}