lunar-lib 0.8.0

Common utilities for lunar applications
Documentation
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 {
    /// Attempts to load a theme from a file, returning the default theme if the file is not found
    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()))
    }
}