use super::error::ConfigError;
use super::store::config;
pub const BUILTIN_THEMES: &[&str] = &["exa", "lx-256", "lx-24bit"];
pub fn is_builtin_theme(name: &str) -> bool {
BUILTIN_THEMES.contains(&name)
}
pub fn all_theme_names() -> Vec<String> {
let mut names: Vec<String> = BUILTIN_THEMES.iter().map(|s| (*s).to_string()).collect();
if let Some(cfg) = config() {
for name in cfg.theme.keys() {
if !names.contains(name) {
names.push(name.clone());
}
}
}
names.sort();
names
}
fn format_theme_toml(name: &str) -> Option<String> {
if is_builtin_theme(name) {
return Some(format!(
"# [theme.{name}] is compiled-in and cannot be dumped as TOML.\n\
# To customise, create a new theme that inherits from it:\n\
#\n\
# [theme.custom]\n\
# inherits = \"{name}\"\n\
# directory = \"bold dodgerblue\"\n\
# date = \"steelblue\""
));
}
let cfg = config()?;
let theme = cfg.theme.get(name)?;
let mut lines = vec![format!("[theme.{name}]")];
if let Some(ref inherits) = theme.inherits {
lines.push(format!("inherits = \"{inherits}\""));
}
if let Some(ref use_style) = theme.use_style {
lines.push(format!("use-style = \"{use_style}\""));
}
let (mut date_keys, mut other_keys): (Vec<_>, Vec<_>) = theme.ui.keys()
.partition(|k| k.as_str() == "date" || k.starts_with("date-"));
other_keys.sort();
date_keys.sort_by(|a, b| {
date_sort_key(a).cmp(&date_sort_key(b)).then_with(|| a.cmp(b))
});
let mut date_block: Vec<String> = Vec::new();
let mut last_group: Option<u8> = None;
for k in &date_keys {
let group = date_sort_key(k).0;
if last_group.is_some_and(|g| g != group) {
date_block.push(String::new());
}
date_block.push(format!("{k} = \"{}\"", theme.ui[*k]));
last_group = Some(group);
}
let insert_at = other_keys.partition_point(|k| k.as_str() < "date");
for k in &other_keys[..insert_at] {
lines.push(format!("{k} = \"{}\"", theme.ui[*k]));
}
if !date_block.is_empty() {
if insert_at > 0 {
lines.push(String::new());
}
lines.extend(date_block);
if insert_at < other_keys.len() {
lines.push(String::new());
}
}
for k in &other_keys[insert_at..] {
lines.push(format!("{k} = \"{}\"", theme.ui[*k]));
}
Some(lines.join("\n"))
}
fn date_sort_key(key: &str) -> (u8, u8) {
const TIERS: [&str; 8] = [
"", "now", "today", "week", "month", "year", "old", "flat",
];
const COLS: [&str; 4] = ["modified", "accessed", "changed", "created"];
if key == "date" {
return (0, 0);
}
let Some(rest) = key.strip_prefix("date-") else {
return (u8::MAX, 0);
};
if let Some(ti) = TIERS.iter().position(|t| *t == rest) {
return (0, ti as u8);
}
if let Some(ci) = COLS.iter().position(|c| *c == rest) {
return ((1 + ci) as u8, 0);
}
for (ci, col) in COLS.iter().enumerate() {
if let Some(tier) = rest.strip_prefix(col).and_then(|r| r.strip_prefix('-'))
&& let Some(ti) = TIERS.iter().position(|t| *t == tier)
{
return ((1 + ci) as u8, ti as u8);
}
}
(5, 0)
}
pub fn dump_theme(name: &str) -> Result<(), ConfigError> {
if let Some(toml) = format_theme_toml(name) {
println!("{toml}");
Ok(())
} else {
Err(ConfigError::NotFound {
kind: "theme",
kind_plural: "themes",
name: name.to_string(),
candidates: all_theme_names().join(", "),
})
}
}
pub fn dump_theme_all() {
let names = all_theme_names();
let mut first = true;
for name in &names {
if let Some(toml) = format_theme_toml(name) {
if !first { println!(); }
println!("{toml}");
first = false;
}
}
}