diff options
-rw-r--r-- | src/config.rs | 388 | ||||
-rw-r--r-- | src/main.rs | 83 |
2 files changed, 213 insertions, 258 deletions
diff --git a/src/config.rs b/src/config.rs index 71a60e53..f32946a3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,45 @@ -use std::fs::File; +use std::{fs::File, path}; use serde_yaml; use tuikit::attr::Color; #[derive(Debug, Clone)] +pub struct Config { + pub colors: Colors, + pub keybindings: Keybindings, + pub terminal: String, + pub opener: String, +} + +impl Config { + pub fn new() -> Self { + Self { + colors: Colors::default(), + keybindings: Keybindings::default(), + terminal: "st".to_owned(), + opener: "xdg-open".to_owned(), + } + } + + pub fn update_from_config(&mut self, yaml: &serde_yaml::value::Value) { + self.colors.update_from_config(&yaml["colors"]); + self.keybindings.update_from_config(&yaml["keybindings"]); + if let Some(terminal) = yaml["terminal"].as_str().map(|s| s.to_string()) { + self.terminal = terminal; + } + if let Some(opener) = yaml["opener"].as_str().map(|s| s.to_string()) { + self.opener = opener; + } + } +} + +impl Default for Config { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, Clone)] pub struct Colors { pub file: String, pub directory: String, @@ -15,47 +51,49 @@ pub struct Colors { } impl Colors { - pub fn from_config(yaml: &serde_yaml::value::Value) -> Self { - let file = yaml["file"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); - let directory = yaml["directory"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); - let block = yaml["block"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); - let char = yaml["char"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); - let fifo = yaml["fifo"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); - let socket = yaml["socket"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); - let symlink = yaml["symlink"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); + pub fn update_from_config(&mut self, yaml: &serde_yaml::value::Value) { + if let Some(file) = yaml["file"].as_str().map(|s| s.to_string()) { + self.file = file; + } + if let Some(directory) = yaml["directory"].as_str().map(|s| s.to_string()) { + self.directory = directory; + } + if let Some(block) = yaml["block"].as_str().map(|s| s.to_string()) { + self.block = block; + } + if let Some(char) = yaml["char"].as_str().map(|s| s.to_string()) { + self.char = char; + } + if let Some(fifo) = yaml["fifo"].as_str().map(|s| s.to_string()) { + self.fifo = fifo; + } + if let Some(socket) = yaml["socket"].as_str().map(|s| s.to_string()) { + self.socket = socket; + } + if let Some(symlink) = yaml["symlink"].as_str().map(|s| s.to_string()) { + self.symlink = symlink; + } + } + + pub fn new() -> Self { Self { - file, - directory, - block, - char, - fifo, - socket, - symlink, + file: "white".to_owned(), + directory: "red".to_owned(), + block: "yellow".to_owned(), + char: "green".to_owned(), + fifo: "blue".to_owned(), + socket: "cyan".to_owned(), + symlink: "magenta".to_owned(), } } } +impl Default for Colors { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug, Clone)] pub struct Keybindings { pub toggle_hidden: char, @@ -82,180 +120,105 @@ pub struct Keybindings { } impl Keybindings { - pub fn new(yaml: &serde_yaml::value::Value) -> Self { - let toggle_hidden = yaml["toggle_hidden"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "a".to_string()) - .chars() - .next() - .unwrap_or('a'); - let copy_paste = yaml["copy_paste"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "c".to_string()) - .chars() - .next() - .unwrap_or('c'); - let cut_paste = yaml["cut_paste"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "p".to_string()) - .chars() - .next() - .unwrap_or('p'); - let delete = yaml["delete"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "x".to_string()) - .chars() - .next() - .unwrap_or('x'); - let chmod = yaml["chmod"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "m".to_string()) - .chars() - .next() - .unwrap_or('m'); - let exec = yaml["exec"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "e".to_string()) - .chars() - .next() - .unwrap_or('e'); - let newdir = yaml["newdir"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "d".to_string()) - .chars() - .next() - .unwrap_or('d'); - let newfile = yaml["newfile"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "n".to_string()) - .chars() - .next() - .unwrap_or('n'); - let rename = yaml["rename"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "r".to_string()) - .chars() - .next() - .unwrap_or('r'); - let clear_flags = yaml["clear_flags"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "u".to_string()) - .chars() - .next() - .unwrap_or('u'); - let toggle_flag = yaml["toggle_flag"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| " ".to_string()) - .chars() - .next() - .unwrap_or(' '); - let shell = yaml["shell"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "s".to_string()) - .chars() - .next() - .unwrap_or('s'); - let open_file = yaml["open_file"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "o".to_string()) - .chars() - .next() - .unwrap_or('o'); - let help = yaml["help"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "h".to_string()) - .chars() - .next() - .unwrap_or('h'); - let search = yaml["search"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "/".to_string()) - .chars() - .next() - .unwrap_or('/'); - let quit = yaml["quit"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "q".to_string()) - .chars() - .next() - .unwrap_or('q'); - let goto = yaml["goto"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "g".to_string()) - .chars() - .next() - .unwrap_or('g'); - let flag_all = yaml["flag_all"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "*".to_string()) - .chars() - .next() - .unwrap_or('*'); - let reverse_flags = yaml["reverse_flags"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "v".to_string()) - .chars() - .next() - .unwrap_or('v'); - let regex_match = yaml["regex_match"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "w".to_string()) - .chars() - .next() - .unwrap_or('w'); - let jump = yaml["jump"] - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| "j".to_string()) - .chars() - .next() - .unwrap_or('j'); + pub fn update_from_config(&mut self, yaml: &serde_yaml::value::Value) { + if let Some(toggle_hidden) = yaml["toggle_hidden"].as_str().map(|s| s.to_string()) { + self.toggle_hidden = toggle_hidden.chars().next().unwrap_or('a'); + } + if let Some(copy_paste) = yaml["copy_paste"].as_str().map(|s| s.to_string()) { + self.copy_paste = copy_paste.chars().next().unwrap_or('c'); + } + if let Some(cut_paste) = yaml["cut_paste"].as_str().map(|s| s.to_string()) { + self.cut_paste = cut_paste.chars().next().unwrap_or('p'); + } + if let Some(delete) = yaml["delete"].as_str().map(|s| s.to_string()) { + self.delete = delete.chars().next().unwrap_or('x'); + } + if let Some(chmod) = yaml["chmod"].as_str().map(|s| s.to_string()) { + self.chmod = chmod.chars().next().unwrap_or('m'); + } + if let Some(exec) = yaml["exec"].as_str().map(|s| s.to_string()) { + self.exec = exec.chars().next().unwrap_or('e'); + } + if let Some(newdir) = yaml["newdir"].as_str().map(|s| s.to_string()) { + self.newdir = newdir.chars().next().unwrap_or('d'); + } + if let Some(newfile) = yaml["newfile"].as_str().map(|s| s.to_string()) { + self.newfile = newfile.chars().next().unwrap_or('n'); + } + if let Some(rename) = yaml["rename"].as_str().map(|s| s.to_string()) { + self.rename = rename.chars().next().unwrap_or('r'); + } + if let Some(clear_flags) = yaml["clear_flags"].as_str().map(|s| s.to_string()) { + self.clear_flags = clear_flags.chars().next().unwrap_or('u'); + } + if let Some(toggle_flag) = yaml["toggle_flag"].as_str().map(|s| s.to_string()) { + self.toggle_flag = toggle_flag.chars().next().unwrap_or(' '); + } + if let Some(shell) = yaml["shell"].as_str().map(|s| s.to_string()) { + self.shell = shell.chars().next().unwrap_or('s'); + } + if let Some(open_file) = yaml["open_file"].as_str().map(|s| s.to_string()) { + self.open_file = open_file.chars().next().unwrap_or('o'); + } + if let Some(help) = yaml["help"].as_str().map(|s| s.to_string()) { + self.help = help.chars().next().unwrap_or('h'); + } + if let Some(search) = yaml["search"].as_str().map(|s| s.to_string()) { + self.search = search.chars().next().unwrap_or('/'); + } + if let Some(quit) = yaml["quit"].as_str().map(|s| s.to_string()) { + self.quit = quit.chars().next().unwrap_or('q'); + } + if let Some(goto) = yaml["goto"].as_str().map(|s| s.to_string()) { + self.goto = goto.chars().next().unwrap_or('g'); + } + if let Some(flag_all) = yaml["flag_all"].as_str().map(|s| s.to_string()) { + self.flag_all = flag_all.chars().next().unwrap_or('*'); + } + if let Some(reverse_flags) = yaml["reverse_flags"].as_str().map(|s| s.to_string()) { + self.reverse_flags = reverse_flags.chars().next().unwrap_or('v'); + } + if let Some(regex_match) = yaml["regex_match"].as_str().map(|s| s.to_string()) { + self.regex_match = regex_match.chars().next().unwrap_or('w'); + } + if let Some(jump) = yaml["jump"].as_str().map(|s| s.to_string()) { + self.jump = jump.chars().next().unwrap_or('j'); + } + } + + pub fn new() -> Self { Self { - toggle_hidden, - copy_paste, - cut_paste, - delete, - chmod, - exec, - newdir, - newfile, - rename, - clear_flags, - toggle_flag, - shell, - open_file, - help, - search, - quit, - goto, - flag_all, - reverse_flags, - regex_match, - jump, + toggle_hidden: 'a', + copy_paste: 'c', + cut_paste: 'p', + delete: 'x', + chmod: 'm', + exec: 'e', + newdir: 'd', + newfile: 'n', + rename: 'r', + clear_flags: 'u', + toggle_flag: ' ', + shell: 's', + open_file: 'o', + help: 'h', + search: '/', + quit: 'q', + goto: 'g', + flag_all: '*', + reverse_flags: 'v', + regex_match: 'w', + jump: 'j', } } } +impl Default for Keybindings { + fn default() -> Self { + Self::new() + } +} + pub fn str_to_tuikit(color: &str) -> Color { match color { "white" => Color::WHITE, @@ -278,7 +241,14 @@ pub fn str_to_tuikit(color: &str) -> Color { } } -pub fn load_file(file: &str) -> serde_yaml::Value { - let file = File::open(file).expect("Unable to open file"); - serde_yaml::from_reader(file).expect("Couldn't read yaml file") +pub fn load_config(path: &str) -> Config { + let mut config = Config::default(); + + if let Ok(file) = File::open(path::Path::new(&shellexpand::tilde(path).to_string())) { + if let Ok(yaml) = serde_yaml::from_reader(file) { + config.update_from_config(&yaml); + } + } + + config } diff --git a/src/main.rs b/src/main.rs index 3bb60a43..cb6c8d3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use tuikit::key::MouseButton; use tuikit::term::{Term, TermHeight}; use fm::args::Args; -use fm::config::{load_file, str_to_tuikit, Colors, Keybindings}; +use fm::config::{load_config, str_to_tuikit, Colors, Config}; use fm::fileinfo::{FileInfo, FileKind, PathContent}; pub mod fileinfo; @@ -25,7 +25,8 @@ const WINDOW_PADDING: usize = 4; const WINDOW_MARGIN_TOP: usize = 1; const EDIT_BOX_OFFSET: usize = 10; const MAX_PERMISSIONS: u32 = 0o777; -static CONFIG_FILE: &str = "/home/quentin/gclem/dev/rust/fm/config.yaml"; + +static CONFIG_PATH: &str = "~/.config/fm/config.yaml"; static USAGE: &str = " FM: dired inspired File Manager @@ -201,31 +202,18 @@ struct Status { path_content: PathContent, height: usize, args: Args, - colors: Colors, - terminal: String, - opener: String, - keybindings: Keybindings, + config: Config, jump_index: usize, } impl Status { - fn new(args: Args, height: usize) -> Self { + fn new(args: Args, config: Config, height: usize) -> Self { let path = std::fs::canonicalize(path::Path::new(&args.path)).unwrap_or_else(|_| { eprintln!("File does not exists {}", args.path); std::process::exit(2) }); let path_content = PathContent::new(path, args.hidden); - let config_file = load_file(CONFIG_FILE); - let colors = Colors::from_config(&config_file["colors"]); - let terminal = config_file["terminal"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); - let opener = config_file["opener"] - .as_str() - .map(|s| s.to_string()) - .expect("Couldn't parse config file"); let mode = Mode::Normal; let file_index = 0; let window = FilesWindow::new(path_content.files.len(), height); @@ -233,7 +221,6 @@ impl Status { let flagged = HashSet::new(); let input_string = "".to_string(); let col = 0; - let keybindings = Keybindings::new(&config_file["keybindings"]); let jump_index = 0; Self { mode, @@ -246,10 +233,7 @@ impl Status { path_content, height, args, - colors, - terminal, - opener, - keybindings, + config, jump_index, } } @@ -401,54 +385,54 @@ impl Status { | Mode::Goto | Mode::RegexMatch => self.event_text_insertion(c), Mode::Normal => { - if c == self.keybindings.toggle_hidden { + if c == self.config.keybindings.toggle_hidden { self.event_toggle_hidden() - } else if c == self.keybindings.copy_paste { + } else if c == self.config.keybindings.copy_paste { self.event_copy_paste() - } else if c == self.keybindings.cut_paste { + } else if c == self.config.keybindings.cut_paste { self.event_cut_paste() - } else if c == self.keybindings.newdir { + } else if c == self.config.keybindings.newdir { self.mode = Mode::Newdir - } else if c == self.keybindings.newfile { + } else if c == self.config.keybindings.newfile { self.mode = Mode::Newfile - } else if c == self.keybindings.chmod { + } else if c == self.config.keybindings.chmod { self.event_chmod() - } else if c == self.keybindings.exec { + } else if c == self.config.keybindings.exec { self.mode = Mode::Exec - } else if c == self.keybindings.goto { + } else if c == self.config.keybindings.goto { self.mode = Mode::Goto - } else if c == self.keybindings.rename { + } else if c == self.config.keybindings.rename { self.event_rename() - } else if c == self.keybindings.clear_flags { + } else if c == self.config.keybindings.clear_flags { self.flagged.clear() - } else if c == self.keybindings.toggle_flag { + } else if c == self.config.keybindings.toggle_flag { self.event_toggle_flag() - } else if c == self.keybindings.shell { + } else if c == self.config.keybindings.shell { self.event_shell() - } else if c == self.keybindings.delete { + } else if c == self.config.keybindings.delete { self.event_delete() - } else if c == self.keybindings.open_file { + } else if c == self.config.keybindings.open_file { self.event_open_file() - } else if c == self.keybindings.help { + } else if c == self.config.keybindings.help { self.mode = Mode::Help - } else if c == self.keybindings.search { + } else if c == self.config.keybindings.search { self.mode = Mode::Search - } else if c == self.keybindings.regex_match { + } else if c == self.config.keybindings.regex_match { self.mode = Mode::RegexMatch - } else if c == self.keybindings.quit { + } else if c == self.config.keybindings.quit { std::process::exit(0) - } else if c == self.keybindings.flag_all { + } else if c == self.config.keybindings.flag_all { self.event_flag_all(); - } else if c == self.keybindings.reverse_flags { + } else if c == self.config.keybindings.reverse_flags { self.event_reverse_flags(); - } else if c == self.keybindings.jump { + } else if c == self.config.keybindings.jump { self.event_jump(); } } Mode::Help => { - if c == self.keybindings.help { + if c == self.config.keybindings.help { self.mode = Mode::Normal - } else if c == self.keybindings.quit { + } else if c == self.config.keybindings.quit { std::process::exit(0); } } @@ -518,7 +502,7 @@ impl Status { fn event_open_file(&mut self) { execute_in_child( - &self.opener, + &self.config.opener, &vec![self.path_content.files[self.path_content.selected] .path .to_str() @@ -562,7 +546,7 @@ impl Status { fn event_shell(&mut self) { execute_in_child( - &self.terminal, + &self.config.terminal, &vec!["-d", self.path_content.path.to_str().unwrap()], ); } @@ -876,7 +860,7 @@ impl Display { .skip(status.window.top) { let row = i + WINDOW_MARGIN_TOP - status.window.top; - let mut attr = fileinfo_attr(&status.path_content.files[i], &status.colors); + let mut attr = fileinfo_attr(&status.path_content.files[i], &status.config.colors); if status.flagged.contains(&status.path_content.files[i].path) { attr.effect |= Effect::UNDERLINE; } @@ -938,7 +922,8 @@ fn main() { let term: Term<()> = Term::with_height(TermHeight::Percent(100)).unwrap(); let _ = term.enable_mouse_support(); let (_, height) = term.term_size().unwrap(); - let mut status = Status::new(args, height); + let config = load_config(CONFIG_PATH); + let mut status = Status::new(args, config, height); let mut display = Display::new(term); while let Ok(ev) = display.term.poll_event() { let _ = display.term.clear(); |