summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/config.rs388
-rw-r--r--src/main.rs83
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();