1use crate::settings::UiSettings;
5use crate::theme::Theme;
6use std::path::{Path, PathBuf};
7
8pub fn resolve_theme_file_path(settings: &UiSettings) -> Option<PathBuf> {
9 let file_name = settings.theme.file.as_deref()?.trim();
10 resolve_theme_file_path_from_name(file_name)
11}
12
13fn themes_dir_path() -> Option<PathBuf> {
14 let home = super::store()?.home().to_path_buf();
15 Some(home.join("themes"))
16}
17
18pub fn list_theme_files() -> Vec<String> {
19 let Some(themes_dir) = themes_dir_path() else {
20 return Vec::new();
21 };
22 let Ok(entries) = std::fs::read_dir(themes_dir) else {
23 return Vec::new();
24 };
25 let mut files: Vec<String> = entries
26 .filter_map(Result::ok)
27 .filter_map(|entry| {
28 let Ok(file_type) = entry.file_type() else {
29 return None;
30 };
31 if !file_type.is_file() {
32 return None;
33 }
34 let name = entry.file_name().into_string().ok()?;
35 if !name.ends_with(".tmTheme") {
36 return None;
37 }
38 Some(name)
39 })
40 .collect();
41 files.sort_unstable();
42 files
43}
44
45pub fn load_theme_file(file_name: &str) -> Theme {
46 let Some(path) = resolve_theme_file_path_from_name(file_name) else {
47 return Theme::default();
48 };
49 Theme::load_from_path(&path)
50}
51
52fn resolve_theme_file_path_from_name(file_name: &str) -> Option<PathBuf> {
53 let trimmed = file_name.trim();
54 if trimmed.is_empty() {
55 return None;
56 }
57 let candidate = Path::new(trimmed);
58 let base_name = candidate.file_name()?.to_str()?;
59 if base_name != trimmed {
60 return None;
61 }
62 if matches!(base_name, "." | "..") {
63 return None;
64 }
65 Some(themes_dir_path()?.join(base_name))
66}