use crate::theme::bundled::BUNDLED_THEMES;
use crate::theme::gogh;
use crate::theme::model::ParsedPalette;
use crate::theme::parse::parse_palette_str;
use anyhow::{Result, anyhow};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Source {
Bundled,
Gogh,
}
impl Source {
pub fn name(self) -> &'static str {
match self {
Source::Bundled => "bundled",
Source::Gogh => gogh::NAME,
}
}
pub fn all() -> &'static [Source] {
&[Source::Bundled, Source::Gogh]
}
pub fn parse(name: &str) -> Option<Source> {
match name {
"bundled" => Some(Source::Bundled),
n if n == gogh::NAME => Some(Source::Gogh),
_ => None,
}
}
pub fn list(self) -> Result<Vec<String>> {
match self {
Source::Bundled => Ok(BUNDLED_THEMES
.iter()
.map(|(n, _)| (*n).to_string())
.collect()),
Source::Gogh => match gogh::cached_names()? {
Some(names) => Ok(names),
None => Err(anyhow!(
"gogh catalog not synced yet — run `colorant themes sync` first"
)),
},
}
}
pub fn sync(self) -> Result<()> {
match self {
Source::Bundled => Ok(()),
Source::Gogh => gogh::sync().map(|_| ()),
}
}
pub fn fetch(self, name: &str) -> Result<ParsedPalette> {
match self {
Source::Bundled => BUNDLED_THEMES
.iter()
.find(|(n, _)| *n == name)
.map(|(_, content)| parse_palette_str(content))
.ok_or_else(|| anyhow!("no bundled theme named {name}")),
Source::Gogh => gogh::fetch(name),
}
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
pub fn parse_ref(s: &str) -> (Option<Source>, &str) {
if let Some((prefix, name)) = s.split_once(':')
&& let Some(source) = Source::parse(prefix)
{
return (Some(source), name);
}
(None, s)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn source_names_round_trip() {
for source in Source::all() {
assert_eq!(Source::parse(source.name()), Some(*source));
}
}
#[test]
fn source_parse_unknown_returns_none() {
assert!(Source::parse("definitely-not-a-source").is_none());
}
#[test]
fn parse_ref_recognizes_source_prefix() {
assert_eq!(parse_ref("gogh:Dracula"), (Some(Source::Gogh), "Dracula"));
assert_eq!(
parse_ref("bundled:catppuccin-mocha"),
(Some(Source::Bundled), "catppuccin-mocha")
);
}
#[test]
fn parse_ref_unqualified_returns_none_source() {
assert_eq!(parse_ref("dracula"), (None, "dracula"));
}
#[test]
fn parse_ref_unknown_prefix_treats_whole_string_as_name() {
assert_eq!(parse_ref("wezterm:Foo"), (None, "wezterm:Foo"));
}
#[test]
fn bundled_list_returns_known_themes() {
let names = Source::Bundled.list().unwrap();
assert!(names.contains(&"catppuccin-mocha".to_string()));
}
}