use super::model::{Mode, ParsedRc, ThemeLayer};
use super::parse;
use anyhow::Result;
use std::path::{Path, PathBuf};
pub const PALETTE_EXTENSION: &str = "colorant";
pub struct Resolver {
pub(crate) base_theme_dir: PathBuf,
}
impl Resolver {
pub fn new(base_theme_dir: PathBuf) -> Self {
Self { base_theme_dir }
}
pub fn resolve(&self, path: &Path, mode: Mode) -> Result<ThemeLayer> {
let rc = parse::parse_rc_file(path)?;
self.flatten(&rc, mode)
}
pub fn flatten(&self, rc: &ParsedRc, mode: Mode) -> Result<ThemeLayer> {
let mut acc = ThemeLayer::default();
if let Some(parent_name) = rc.parent_for(mode) {
let palette_path =
self.base_theme_dir
.join(format!("{}.{}", parent_name.as_str(), PALETTE_EXTENSION));
if palette_path.exists() {
let palette = parse::parse_palette_file(&palette_path)?;
acc.merge(&palette.layer);
}
}
acc.merge(&rc.base);
match mode {
Mode::Dark => acc.merge(&rc.dark),
Mode::Light => acc.merge(&rc.light),
Mode::Unknown => {}
}
Ok(acc)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::theme::model::{HexColor, ThemeName};
use tempfile::tempdir;
fn layer_with_fg(hex: &str) -> ThemeLayer {
ThemeLayer {
fg: HexColor::parse(hex),
..Default::default()
}
}
#[test]
fn unknown_mode_skips_per_mode_layers_and_extends() {
let dir = tempdir().unwrap();
std::fs::write(
dir.path().join("base.colorant"),
"fg = #aaaaaa\nbg = #bbbbbb\n",
)
.unwrap();
std::fs::write(dir.path().join("dark.colorant"), "fg = #000000\n").unwrap();
let rc = ParsedRc {
extends: Some(ThemeName::parse("base").unwrap()),
extends_dark: Some(ThemeName::parse("dark").unwrap()),
base: layer_with_fg("#cccccc"),
dark: layer_with_fg("#111111"),
light: layer_with_fg("#eeeeee"),
..Default::default()
};
let resolver = Resolver::new(dir.path().to_path_buf());
let resolved = resolver.flatten(&rc, Mode::Unknown).unwrap();
assert_eq!(resolved.fg.as_ref().unwrap().as_str(), "#cccccc");
assert_eq!(resolved.bg.as_ref().unwrap().as_str(), "#bbbbbb");
}
}