summaryrefslogtreecommitdiffstats
path: root/src/config
diff options
context:
space:
mode:
authorCaleb Bassi <calebjbassi@gmail.com>2019-02-15 04:43:09 -0800
committerCaleb Bassi <calebjbassi@gmail.com>2019-02-15 05:56:09 -0800
commit29b3922f9efb9f277641a63f8f1719a15cbb8d16 (patch)
treed919ba11fa5c754565c64c43cb1614f7ae4d2546 /src/config
parent3853eef2d052460982903038daac7abd2b71d12e (diff)
refactor: project layout
Diffstat (limited to 'src/config')
-rw-r--r--src/config/config.rs125
-rw-r--r--src/config/keymap.rs152
-rw-r--r--src/config/mimetype.rs87
-rw-r--r--src/config/mod.rs61
-rw-r--r--src/config/preview.rs61
-rw-r--r--src/config/theme.rs230
6 files changed, 716 insertions, 0 deletions
diff --git a/src/config/config.rs b/src/config/config.rs
new file mode 100644
index 0000000..b970c16
--- /dev/null
+++ b/src/config/config.rs
@@ -0,0 +1,125 @@
+extern crate toml;
+extern crate whoami;
+extern crate xdg;
+
+use config::{parse_config_file, Flattenable};
+use sort;
+
+use CONFIG_FILE;
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct SortRawOption {
+ pub show_hidden: Option<bool>,
+ pub directories_first: Option<bool>,
+ pub case_sensitive: Option<bool>,
+ pub reverse: Option<bool>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct JoshutoRawConfig {
+ scroll_offset: Option<usize>,
+ tilde_in_titlebar: Option<bool>,
+ sort_type: Option<String>,
+ sort_option: Option<SortRawOption>,
+ column_ratio: Option<[usize; 3]>,
+}
+
+impl JoshutoRawConfig {
+ #[allow(dead_code)]
+ pub fn new() -> Self {
+ JoshutoRawConfig {
+ scroll_offset: Some(8),
+ tilde_in_titlebar: Some(true),
+ sort_type: Some(String::from("natural")),
+ sort_option: None,
+ column_ratio: Some([1, 3, 4]),
+ }
+ }
+}
+
+impl Flattenable<JoshutoConfig> for JoshutoRawConfig {
+ fn flatten(self) -> JoshutoConfig {
+ let column_ratio = match self.column_ratio {
+ Some(s) => (s[0], s[1], s[2]),
+ None => (1, 3, 4),
+ };
+
+ let scroll_offset: usize = self.scroll_offset.unwrap_or(6);
+ let tilde_in_titlebar: bool = self.tilde_in_titlebar.unwrap_or(true);
+
+ let show_hidden: bool;
+ let case_sensitive: bool;
+ let reverse: bool;
+ let directories_first: bool;
+
+ match self.sort_option {
+ Some(s) => {
+ show_hidden = s.show_hidden.unwrap_or(false);
+ case_sensitive = s.case_sensitive.unwrap_or(false);
+ reverse = s.reverse.unwrap_or(false);
+ directories_first = s.directories_first.unwrap_or(true);
+ }
+ None => {
+ show_hidden = false;
+ case_sensitive = false;
+ reverse = false;
+ directories_first = true;
+ }
+ }
+
+ let sort_option = sort::SortOption {
+ show_hidden,
+ directories_first,
+ case_sensitive,
+ reverse,
+ };
+
+ let sort_type: sort::SortType = match self.sort_type {
+ Some(s) => match s.as_str() {
+ "natural" => sort::SortType::SortNatural(sort_option),
+ "mtime" => sort::SortType::SortMtime(sort_option),
+ _ => sort::SortType::SortNatural(sort_option),
+ },
+ _ => sort::SortType::SortNatural(sort_option),
+ };
+
+ JoshutoConfig {
+ scroll_offset,
+ tilde_in_titlebar,
+ sort_type,
+ column_ratio,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct JoshutoConfig {
+ pub scroll_offset: usize,
+ pub tilde_in_titlebar: bool,
+ pub sort_type: sort::SortType,
+ pub column_ratio: (usize, usize, usize),
+}
+
+impl JoshutoConfig {
+ pub fn new() -> Self {
+ let sort_option = sort::SortOption {
+ show_hidden: false,
+ directories_first: true,
+ case_sensitive: false,
+ reverse: false,
+ };
+ let sort_type = sort::SortType::SortNatural(sort_option);
+
+ JoshutoConfig {
+ scroll_offset: 6,
+ tilde_in_titlebar: true,
+ sort_type,
+ column_ratio: (1, 3, 4),
+ }
+ }
+
+ pub fn get_config() -> JoshutoConfig {
+ parse_config_file::<JoshutoRawConfig, JoshutoConfig>(CONFIG_FILE)
+ .unwrap_or_else(|| JoshutoConfig::new())
+ }
+}
diff --git a/src/config/keymap.rs b/src/config/keymap.rs
new file mode 100644
index 0000000..639c33c
--- /dev/null
+++ b/src/config/keymap.rs
@@ -0,0 +1,152 @@
+extern crate fs_extra;
+extern crate toml;
+extern crate xdg;
+
+use std::collections::HashMap;
+use std::process::exit;
+
+use commands;
+use config::{parse_config_file, Flattenable};
+use KEYMAP_FILE;
+
+pub const BACKSPACE: i32 = 0x7F;
+pub const TAB: i32 = 0x9;
+pub const ENTER: i32 = 0xA;
+pub const ESCAPE: i32 = 0x1B;
+
+/* #define KEY_ALT(x) KEY_F(60) + (x - 'A') */
+
+#[derive(Debug, Deserialize)]
+struct JoshutoMapCommand {
+ pub keys: Vec<String>,
+ pub command: String,
+ pub args: Option<Vec<String>>,
+}
+
+#[derive(Debug, Deserialize)]
+struct JoshutoRawKeymap {
+ mapcommand: Option<Vec<JoshutoMapCommand>>,
+}
+
+impl Flattenable<JoshutoKeymap> for JoshutoRawKeymap {
+ fn flatten(self) -> JoshutoKeymap {
+ let mut keymaps: HashMap<i32, commands::CommandKeybind> = HashMap::new();
+ if let Some(maps) = self.mapcommand {
+ for mapcommand in maps {
+ match commands::from_args(mapcommand.command.as_str(), mapcommand.args.as_ref()) {
+ Some(command) => {
+ insert_keycommand(&mut keymaps, command, &mapcommand.keys[..]);
+ }
+ None => {
+ println!("Unknown command: {}", mapcommand.command);
+ }
+ }
+ }
+ }
+ JoshutoKeymap { keymaps }
+ }
+}
+
+#[derive(Debug)]
+pub struct JoshutoKeymap {
+ pub keymaps: HashMap<i32, commands::CommandKeybind>,
+}
+
+impl JoshutoKeymap {
+ pub fn new() -> Self {
+ let keymaps = HashMap::new();
+ JoshutoKeymap { keymaps }
+ }
+
+ pub fn get_config() -> JoshutoKeymap {
+ parse_config_file::<JoshutoRawKeymap, JoshutoKeymap>(KEYMAP_FILE)
+ .unwrap_or_else(|| JoshutoKeymap::new())
+ }
+}
+
+fn insert_keycommand(
+ map: &mut HashMap<i32, commands::CommandKeybind>,
+ keycommand: Box<dyn commands::JoshutoCommand>,
+ keys: &[String],
+) {
+ if keys.len() == 1 {
+ match key_to_i32(&keys[0]) {
+ Some(s) => {
+ map.insert(s, commands::CommandKeybind::SimpleKeybind(keycommand));
+ }
+ None => {}
+ }
+ } else {
+ match key_to_i32(&keys[0]) {
+ Some(s) => {
+ let mut new_map: HashMap<i32, commands::CommandKeybind>;
+ match map.remove(&s) {
+ Some(commands::CommandKeybind::CompositeKeybind(mut m)) => {
+ new_map = m;
+ }
+ Some(_) => {
+ eprintln!("Error: Keybindings ambiguous");
+ exit(1);
+ }
+ None => {
+ new_map = HashMap::new();
+ }
+ }
+ insert_keycommand(&mut new_map, keycommand, &keys[1..]);
+ let composite_command = commands::CommandKeybind::CompositeKeybind(new_map);
+ map.insert(s as i32, composite_command);
+ }
+ None => {}
+ }
+ }
+}
+
+pub fn key_to_i32(keycode: &str) -> Option<i32> {
+ if keycode.len() == 1 {
+ for ch in keycode.chars() {
+ if ch.is_ascii() {
+ return Some(ch as i32);
+ }
+ }
+ return None;
+ } else {
+ match keycode {
+ "Tab" => Some(TAB),
+ "Space" => Some(' ' as i32),
+ "Backspace" => Some(BACKSPACE),
+ "Delete" => Some(ncurses::KEY_DC),
+ "Enter" => Some(ENTER),
+ "Escape" => Some(ESCAPE),
+
+ "F0" => Some(ncurses::KEY_F0),
+ "F1" => Some(ncurses::KEY_F1),
+ "F2" => Some(ncurses::KEY_F2),
+ "F3" => Some(ncurses::KEY_F3),
+ "F4" => Some(ncurses::KEY_F4),
+ "F5" => Some(ncurses::KEY_F5),
+ "F6" => Some(ncurses::KEY_F6),
+ "F7" => Some(ncurses::KEY_F7),
+ "F8" => Some(ncurses::KEY_F8),
+ "F9" => Some(ncurses::KEY_F9),
+ "F10" => Some(ncurses::KEY_F10),
+ "F11" => Some(ncurses::KEY_F11),
+ "F12" => Some(ncurses::KEY_F12),
+ "F13" => Some(ncurses::KEY_F13),
+ "F14" => Some(ncurses::KEY_F14),
+ "F15" => Some(ncurses::KEY_F15),
+
+ "Insert" => Some(ncurses::KEY_IC), /* insert-character key */
+ "PageUp" => Some(ncurses::KEY_PPAGE), /* next-page key */
+ "PageDown" => Some(ncurses::KEY_NPAGE), /* previous-page key */
+ "PrintScreen" => Some(ncurses::KEY_PRINT), /* print key */
+
+ "Up" => Some(ncurses::KEY_UP),
+ "Down" => Some(ncurses::KEY_DOWN),
+ "Left" => Some(ncurses::KEY_LEFT),
+ "Right" => Some(ncurses::KEY_RIGHT),
+ "Home" => Some(ncurses::KEY_HOME),
+ "End" => Some(ncurses::KEY_END),
+ _ => None,
+ }
+ }
+}
diff --git a/src/config/mimetype.rs b/src/config/mimetype.rs
new file mode 100644
index 0000000..1be211f
--- /dev/null
+++ b/src/config/mimetype.rs
@@ -0,0 +1,87 @@
+extern crate toml;
+extern crate xdg;
+
+use std::collections::HashMap;
+use std::fmt;
+
+use config::{parse_config_file, Flattenable};
+use MIMETYPE_FILE;
+
+#[derive(Debug, Deserialize)]
+pub struct JoshutoMimetypeEntry {
+ pub program: String,
+ pub args: Option<Vec<String>>,
+ pub fork: Option<bool>,
+ pub silent: Option<bool>,
+}
+
+impl std::fmt::Display for JoshutoMimetypeEntry {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.program.as_str()).unwrap();
+ if let Some(s) = self.args.as_ref() {
+ for arg in s {
+ write!(f, " {}", arg).unwrap();
+ }
+ }
+ f.write_str("\t[").unwrap();
+ if let Some(s) = self.fork {
+ if s {
+ f.write_str("fork,").unwrap();
+ }
+ }
+ if let Some(s) = self.silent {
+ if s {
+ f.write_str("silent").unwrap();
+ }
+ }
+ f.write_str("]")
+ }
+}
+
+#[derive(Debug, Deserialize)]
+pub struct JoshutoRawMimetype {
+ mimetype: Option<HashMap<String, Vec<JoshutoMimetypeEntry>>>,
+ extension: Option<HashMap<String, Vec<JoshutoMimetypeEntry>>>,
+}
+
+impl JoshutoRawMimetype {
+ #[allow(dead_code)]
+ pub fn new() -> Self {
+ JoshutoRawMimetype {
+ mimetype: None,
+ extension: None,
+ }
+ }
+}
+
+impl Flattenable<JoshutoMimetype> for JoshutoRawMimetype {
+ fn flatten(self) -> JoshutoMimetype {
+ let mimetype = self.mimetype.unwrap_or(HashMap::new());
+ let extension = self.extension.unwrap_or(HashMap::new());
+
+ JoshutoMimetype {
+ mimetype,
+ extension,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct JoshutoMimetype {
+ pub mimetype: HashMap<String, Vec<JoshutoMimetypeEntry>>,
+ pub extension: HashMap<String, Vec<JoshutoMimetypeEntry>>,
+}
+
+impl JoshutoMimetype {
+ pub fn new() -> Self {
+ JoshutoMimetype {
+ mimetype: HashMap::new(),
+ extension: HashMap::new(),
+ }
+ }
+
+ pub fn get_config() -> JoshutoMimetype {
+ parse_config_file::<JoshutoRawMimetype, JoshutoMimetype>(MIMETYPE_FILE)
+ .unwrap_or_else(|| JoshutoMimetype::new())
+ }
+}
diff --git a/src/config/mod.rs b/src/config/mod.rs
new file mode 100644
index 0000000..273b9ab
--- /dev/null
+++ b/src/config/mod.rs
@@ -0,0 +1,61 @@
+extern crate serde;
+
+pub mod config;
+pub mod keymap;
+pub mod mimetype;
+pub mod preview;
+pub mod theme;
+
+pub use self::config::JoshutoConfig;
+pub use self::keymap::JoshutoKeymap;
+pub use self::mimetype::JoshutoMimetype;
+pub use self::preview::JoshutoPreview;
+pub use self::theme::{JoshutoColorTheme, JoshutoTheme};
+
+use self::serde::de::DeserializeOwned;
+use std::fs;
+use std::path::{Path, PathBuf};
+
+use CONFIG_HIERARCHY;
+
+// implemented by config file implementations to turn a RawConfig into a Config
+trait Flattenable<T> {
+ fn flatten(self) -> T;
+}
+
+// searches a list of folders for a given file in order of preference
+pub fn search_directories<P>(filename: &str, directories: &[P]) -> Option<PathBuf>
+where
+ P: AsRef<Path>,
+{
+ for path in directories.iter() {
+ let filepath = path.as_ref().join(filename);
+ if filepath.exists() {
+ return Some(filepath);
+ }
+ }
+ None
+}
+
+// parses a config file into its appropriate format
+fn parse_config_file<T, S>(filename: &str) -> Option<S>
+where
+ T: DeserializeOwned + Flattenable<S>,
+{
+ let file_path = search_directories(filename, &CONFIG_HIERARCHY)?;
+ let file_contents = match fs::read_to_string(&file_path) {
+ Ok(content) => content,
+ Err(e) => {
+ eprintln!("Error reading {} file: {}", filename, e);
+ return None;
+ }
+ };
+ let config = match toml::from_str::<T>(&file_contents) {
+ Ok(config) => config,
+ Err(e) => {
+ eprintln!("Error parsing {} file: {}", filename, e);
+ return None;
+ }
+ };
+ Some(config.flatten())
+}
diff --git a/src/config/preview.rs b/src/config/preview.rs
new file mode 100644
index 0000000..d8f3072
--- /dev/null
+++ b/src/config/preview.rs
@@ -0,0 +1,61 @@
+extern crate toml;
+extern crate xdg;
+
+use std::collections::HashMap;
+
+use config::{parse_config_file, Flattenable};
+use PREVIEW_FILE;
+
+#[derive(Debug, Deserialize)]
+pub struct JoshutoPreviewEntry {
+ pub program: String,
+ pub args: Option<Vec<String>>,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct JoshutoRawPreview {
+ pub mimetype: Option<HashMap<String, JoshutoPreviewEntry>>,
+ pub extension: Option<HashMap<String, JoshutoPreviewEntry>>,
+}
+
+impl JoshutoRawPreview {
+ #[allow(dead_code)]
+ pub fn new() -> Self {
+ JoshutoRawPreview {
+ mimetype: None,
+ extension: None,
+ }
+ }
+}
+
+impl Flattenable<JoshutoPreview> for JoshutoRawPreview {
+ fn flatten(self) -> JoshutoPreview {
+ let mimetype = self.mimetype.unwrap_or(HashMap::new());
+ let extension = self.extension.unwrap_or(HashMap::new());
+
+ JoshutoPreview {
+ mimetype,
+ extension,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct JoshutoPreview {
+ pub mimetype: HashMap<String, JoshutoPreviewEntry>,
+ pub extension: HashMap<String, JoshutoPreviewEntry>,
+}
+
+impl JoshutoPreview {
+ pub fn new() -> Self {
+ JoshutoPreview {
+ mimetype: HashMap::new(),
+ extension: HashMap::new(),
+ }
+ }
+
+ pub fn get_config() -> JoshutoPreview {
+ parse_config_file::<JoshutoRawPreview, JoshutoPreview>(PREVIEW_FILE)
+ .unwrap_or_else(|| JoshutoPreview::new())
+ }
+}
diff --git a/src/config/theme.rs b/src/config/theme.rs
new file mode 100644
index 0000000..2e27663
--- /dev/null
+++ b/src/config/theme.rs
@@ -0,0 +1,230 @@
+extern crate toml;
+extern crate xdg;
+
+use std::collections::HashMap;
+
+use config::{parse_config_file, Flattenable};
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct JoshutoColorPair {
+ pub id: i16,
+ pub fg: i16,
+ pub bg: i16,
+}
+
+impl JoshutoColorPair {
+ pub fn new(id: i16, fg: i16, bg: i16) -> Self {
+ JoshutoColorPair { id, fg, bg }
+ }
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct JoshutoRawColorTheme {
+ pub colorpair: i16,
+ pub bold: Option<bool>,
+ pub underline: Option<bool>,
+ pub prefix: Option<String>,
+ pub prefixsize: Option<usize>,
+}
+
+impl JoshutoRawColorTheme {
+ pub fn flatten(self) -> JoshutoColorTheme {
+ JoshutoColorTheme {
+ colorpair: self.colorpair,
+ bold: self.bold.unwrap_or(false),
+ underline: self.underline.unwrap_or(false),
+ prefix: self.prefix,
+ prefixsize: self.prefixsize,
+ }
+ }
+}
+
+#[derive(Debug, Deserialize)]
+pub struct JoshutoRawTheme {
+ colorpair: Option<Vec<JoshutoColorPair>>,
+ selection: Option<JoshutoRawColorTheme>,
+ executable: Option<JoshutoRawColorTheme>,
+ regular: Option<JoshutoRawColorTheme>,
+ directory: Option<JoshutoRawColorTheme>,
+ link: Option<JoshutoRawColorTheme>,
+ socket: Option<JoshutoRawColorTheme>,
+ ext: Option<HashMap<String, JoshutoRawColorTheme>>,
+}
+
+impl Flattenable<JoshutoTheme> for JoshutoRawTheme {
+ fn flatten(self) -> JoshutoTheme {
+ let colorpair = match self.colorpair {
+ Some(s) => s,
+ None => {
+ let mut colorpair: Vec<JoshutoColorPair> = Vec::with_capacity(10);
+ colorpair.push(JoshutoColorPair::new(2, 2, -1));
+ colorpair.push(JoshutoColorPair::new(3, 3, -1));
+ colorpair.push(JoshutoColorPair::new(4, 4, -1));
+ colorpair.push(JoshutoColorPair::new(5, 5, -1));
+ colorpair.push(JoshutoColorPair::new(6, 6, -1));
+ colorpair
+ }
+ };
+
+ let selection = self.selection.unwrap_or(JoshutoRawColorTheme {
+ colorpair: 3,
+ bold: Some(true),
+ underline: None,
+ prefix: None,
+ prefixsize: None,
+ });
+
+ let executable = self.executable.unwrap_or(JoshutoRawColorTheme {
+ colorpair: 2,
+ bold: Some(true),
+ underline: None,
+ prefix: None,
+ prefixsize: None,
+ });
+
+ let regular = self.regular.unwrap_or(JoshutoRawColorTheme {
+ colorpair: 0,
+ bold: None,
+ underline: None,
+ prefix: None,
+ prefixsize: None,
+ });
+
+ let directory = self.directory.unwrap_or(JoshutoRawColorTheme {
+ colorpair: 4,
+ bold: Some(true),
+ underline: None,
+ prefix: None,
+ prefixsize: None,
+ });
+
+ let link = self.link.unwrap_or(JoshutoRawColorTheme {
+ colorpair: 6,
+ bold: Some(true),
+ underline: None,
+ prefix: None,
+ prefixsize: None,
+ });
+
+ let socket = self.socket.unwrap_or(JoshutoRawColorTheme {
+ colorpair: 6,
+ bold: Some(true),
+ underline: None,
+ prefix: None,
+ prefixsize: None,
+ });
+
+ let mut extraw = self.ext.unwrap_or(HashMap::new());
+ let mut ext: HashMap<String, JoshutoColorTheme> = HashMap::new();
+ for (k, v) in extraw.drain() {
+ ext.insert(k, v.flatten());
+ }
+
+ JoshutoTheme {
+ colorpair,
+ regular: regular.flatten(),
+ directory: directory.flatten(),
+ selection: selection.flatten(),
+ executable: executable.flatten(),
+ link: link.flatten(),
+ socket: socket.flatten(),
+ ext,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct JoshutoColorTheme {
+ pub colorpair: i16,
+ pub bold: bool,
+ pub underline: bool,
+ pub prefix: Option<String>,
+ pub prefixsize: Option<usize>,
+}
+
+#[derive(Debug, Clone)]
+pub struct JoshutoTheme {
+ pub colorpair: Vec<JoshutoColorPair>,
+ pub regular: JoshutoColorTheme,
+ pub selection: JoshutoColorTheme,
+ pub directory: JoshutoColorTheme,
+ pub executable: JoshutoColorTheme,
+ pub link: JoshutoColorTheme,
+ pub socket: JoshutoColorTheme,
+ pub ext: HashMap<String, JoshutoColorTheme>,
+}
+
+impl JoshutoTheme {
+ pub fn new() -> Self {
+ let mut colorpair: Vec<JoshutoColorPair> = Vec::with_capacity(10);
+ colorpair.push(JoshutoColorPair::new(2, 2, -1));
+ colorpair.push(JoshutoColorPair::new(3, 3, -1));
+ colorpair.push(JoshutoColorPair::new(4, 4, -1));
+ colorpair.push(JoshutoColorPair::new(5, 5, -1));
+ colorpair.push(JoshutoColorPair::new(6, 6, -1));
+
+ let selection = JoshutoColorTheme {
+ colorpair: 3,
+ bold: true,
+ underline: false,
+ prefix: None,
+ prefixsize: None,
+ };
+
+ let executable = JoshutoColorTheme {
+ colorpair: 2,
+ bold: true,
+ underline: false,
+ prefix: None,
+ prefixsize: None,
+ };
+
+ let regular = JoshutoColorTheme {
+ colorpair: 0,
+ bold: false,
+ underline: false,
+ prefix: None,
+ prefixsize: None,
+ };
+
+ let directory = JoshutoColorTheme {
+ colorpair: 4,
+ bold: true,
+ underline: false,
+ prefix: None,
+ prefixsize: None,
+ };
+
+ let link = JoshutoColorTheme {
+ colorpair: 6,
+ bold: true,
+ underline: false,
+ prefix: None,
+ prefixsize: None,
+ };
+
+ let socket = JoshutoColorTheme {
+ colorpair: 6,
+ bold: true,
+ underline: false,
+ prefix: None,
+ prefixsize: None,
+ };
+
+ JoshutoTheme {
+ colorpair,
+ selection,
+ executable,
+ regular,
+ directory,
+ link,
+ socket,
+ ext: HashMap::new(),
+ }
+ }
+
+ pub fn get_config() -> JoshutoTheme {
+ parse_config_file::<JoshutoRawTheme, JoshutoTheme>(::THEME_FILE)
+ .unwrap_or_else(|| JoshutoTheme::new())
+ }
+}