use std::{collections::HashMap, io, path::Path};
use ratatui::style::{Color, Style};
use thiserror::Error;
use crate::{iterator_ext::IteratorExtensions, ratatui_theme::raw_theme::RawTheme};
mod raw_theme;
pub struct ResolvedPalette {
styles: HashMap<String, Style>,
palette: HashMap<String, Color>,
}
impl ResolvedPalette {
pub fn palette(&self) -> &HashMap<String, Color> {
&self.palette
}
pub fn resolve_style(&self, key: &str) -> Style {
let parts = key.split('.').to_vec();
for i in (0..=parts.len()).rev() {
let key = parts[0..i].join(".");
if let Some(style) = self.styles.get(&key) {
return *style;
}
}
Style::default()
}
pub fn resolve_style_group(&self, root_key: &str) -> HashMap<String, Style> {
let root_key = format!("{root_key}.");
let mut styles = HashMap::new();
for (key, style) in self.styles.iter() {
if let Some(key) = key.strip_prefix(&root_key) {
styles.insert(key.to_owned(), *style);
};
}
styles
}
}
#[derive(Debug, Error)]
pub enum ThemeError {
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Deserialize(#[from] toml::de::Error),
}
pub trait Theme: From<ResolvedPalette> + Default {
fn load(path: impl AsRef<Path>) -> Result<Self, ThemeError> {
let theme = match std::fs::read_to_string(path.as_ref()) {
Ok(toml) => {
let raw_theme: RawTheme = toml::from_str(&toml)?;
Self::from(raw_theme.resolve())
}
Err(err) if matches![err.kind(), io::ErrorKind::NotFound] => Self::default(),
Err(err) => return Err(err.into()),
};
Ok(theme)
}
fn from_str(s: &str) -> Result<Self, ThemeError> {
let raw_theme: RawTheme = toml::from_str(s)?;
Ok(Self::from(raw_theme.resolve()))
}
}