1 use super::theme::Theme;
2 use super::settings::*;
3 use super::super::LoadingError;
4 use std::collections::BTreeMap;
5 use std::path::{Path, PathBuf};
6 use std::io::{BufReader, BufRead, Seek};
7 use walkdir::WalkDir;
8 use std::fs::File;
9 
10 #[derive(Debug, Default, Serialize, Deserialize)]
11 pub struct ThemeSet {
12     // This is a `BTreeMap` because they're faster than hashmaps on small sets
13     pub themes: BTreeMap<String, Theme>,
14 }
15 
16 /// A set of themes, includes convenient methods for loading and discovering themes.
17 impl ThemeSet {
18     /// Creates an empty set
new() -> ThemeSet19     pub fn new() -> ThemeSet {
20         ThemeSet::default()
21     }
22 
23     /// Returns all the themes found in a folder
24     ///
25     /// This is god for enumerating before loading one with [`get_theme`](#method.get_theme)
discover_theme_paths<P: AsRef<Path>>(folder: P) -> Result<Vec<PathBuf>, LoadingError>26     pub fn discover_theme_paths<P: AsRef<Path>>(folder: P) -> Result<Vec<PathBuf>, LoadingError> {
27         let mut themes = Vec::new();
28         for entry in WalkDir::new(folder) {
29             let entry = entry.map_err(LoadingError::WalkDir)?;
30             if entry.path().extension().map_or(false, |e| e == "tmTheme") {
31                 themes.push(entry.path().to_owned());
32             }
33         }
34         Ok(themes)
35     }
36 
37     /// Loads a theme given a path to a .tmTheme file
get_theme<P: AsRef<Path>>(path: P) -> Result<Theme, LoadingError>38     pub fn get_theme<P: AsRef<Path>>(path: P) -> Result<Theme, LoadingError> {
39         let file = File::open(path)?;
40         let mut file = BufReader::new(file);
41         Self::load_from_reader(&mut file)
42     }
43 
44     /// Loads a theme given a readable stream
load_from_reader<R: BufRead + Seek>(r: &mut R) -> Result<Theme, LoadingError>45     pub fn load_from_reader<R: BufRead + Seek>(r: &mut R) -> Result<Theme, LoadingError> {
46         Ok(Theme::parse_settings(read_plist(r)?)?)
47     }
48 
49     /// Generate a `ThemeSet` from all themes in a folder
load_from_folder<P: AsRef<Path>>(folder: P) -> Result<ThemeSet, LoadingError>50     pub fn load_from_folder<P: AsRef<Path>>(folder: P) -> Result<ThemeSet, LoadingError> {
51         let mut theme_set = Self::new();
52         theme_set.add_from_folder(folder)?;
53         Ok(theme_set)
54     }
55 
56     /// Load all the themes in the folder into this `ThemeSet`
add_from_folder<P: AsRef<Path>>(&mut self, folder: P) -> Result<(), LoadingError>57     pub fn add_from_folder<P: AsRef<Path>>(&mut self, folder: P) -> Result<(), LoadingError> {
58         let paths = Self::discover_theme_paths(folder)?;
59         for p in &paths {
60             let theme = Self::get_theme(p)?;
61             let basename =
62                 p.file_stem().and_then(|x| x.to_str()).ok_or(LoadingError::BadPath)?;
63             self.themes.insert(basename.to_owned(), theme);
64         }
65 
66         Ok(())
67     }
68 }
69 
70 
71 #[cfg(test)]
72 mod tests {
73     use crate::highlighting::{ThemeSet, Color};
74     #[test]
can_parse_common_themes()75     fn can_parse_common_themes() {
76         let themes = ThemeSet::load_from_folder("testdata").unwrap();
77         let all_themes: Vec<&str> = themes.themes.keys().map(|x| &**x).collect();
78         assert!(all_themes.contains(&"base16-ocean.dark"));
79 
80         println!("{:?}", all_themes);
81 
82         let theme = ThemeSet::get_theme("testdata/spacegray/base16-ocean.dark.tmTheme").unwrap();
83         assert_eq!(theme.name.unwrap(), "Base16 Ocean Dark");
84         assert_eq!(theme.settings.selection.unwrap(),
85                    Color {
86                        r: 0x4f,
87                        g: 0x5b,
88                        b: 0x66,
89                        a: 0xff,
90                    });
91         assert_eq!(theme.scopes[0].style.foreground.unwrap(),
92                    Color {
93                        r: 0xc0,
94                        g: 0xc5,
95                        b: 0xce,
96                        a: 0xFF,
97                    });
98         // assert!(false);
99     }
100 }
101