Reworked config parsing and added error handling
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
use std::fs;
|
||||
use std::str;
|
||||
use std::path::PathBuf;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use toml::Table;
|
||||
|
||||
use crate::dotfile::Dotfile;
|
||||
@@ -9,60 +10,139 @@ use crate::dotfile::Dotfile;
|
||||
|
||||
pub struct Config {
|
||||
pub manager_dir: PathBuf,
|
||||
pub dotfiles: Vec<Dotfile>,
|
||||
pub dotfiles: Vec<Result<Dotfile, Box<dyn Error>>>,
|
||||
}
|
||||
|
||||
|
||||
impl Config {
|
||||
pub fn parse(config: PathBuf) -> Result<Self, Box<dyn Error>> {
|
||||
pub fn parse(path: PathBuf) -> Result<Self, Box<dyn Error>> {
|
||||
|
||||
let config_file: Table = str::from_utf8(&fs::read(config)?)?.parse()?;
|
||||
let config_file = Config::read_config(path)?;
|
||||
|
||||
let read_dotfiles = config_file.get("dotfiles").expect("No dotfiles section in config");
|
||||
let dotfiles = Config::get_dotfiles(&config_file)?;
|
||||
|
||||
let dotfile_array = read_dotfiles.as_array().expect("Invalid config file format").iter();
|
||||
let manager_dir = Config::get_manager_dir(&config_file);
|
||||
|
||||
let dotfiles = dotfile_array.map(|dotfile| {
|
||||
Ok(Config{manager_dir, dotfiles})
|
||||
}
|
||||
|
||||
|
||||
fn read_config(path: PathBuf) -> Result<Table, Box<dyn Error>> {
|
||||
|
||||
let file = fs::read(path)?;
|
||||
|
||||
let read_file = String::from_utf8(file)?;
|
||||
|
||||
let config: Table = read_file.parse()?;
|
||||
|
||||
Ok(config)
|
||||
|
||||
}
|
||||
|
||||
|
||||
fn get_dotfiles(config: &Table) -> Result<Vec<Result<Dotfile, Box<dyn Error>>>, Box<dyn Error>> {
|
||||
|
||||
let read_dotfiles = config.get("dotfiles");
|
||||
|
||||
let dotfiles = match read_dotfiles {
|
||||
Some(dotfiles) => dotfiles,
|
||||
None => return Err(Config::produce_error(1)),
|
||||
};
|
||||
|
||||
let dotfile_iter = match dotfiles.as_array() {
|
||||
Some(dotfiles) => dotfiles.iter(),
|
||||
None => return Err(Config::produce_error(2)),
|
||||
};
|
||||
|
||||
|
||||
let dotfiles = dotfile_iter.map(|dotfile| {
|
||||
|
||||
let dotfile_table = dotfile.as_table().unwrap();
|
||||
|
||||
let manager_path = PathBuf::from(
|
||||
match dotfile_table.get("manager_path") {
|
||||
Some(path) => path.as_str().expect("Invalid character in dotfile path"),
|
||||
None => return None,
|
||||
None => return Err(Config::produce_error(3)),
|
||||
}
|
||||
);
|
||||
|
||||
let system_path = PathBuf::from(
|
||||
match dotfile_table.get("system_path") {
|
||||
Some(path) => path.as_str().expect("Invalid character in dotfile path"),
|
||||
None => return None,
|
||||
None => return Err(Config::produce_error(3)),
|
||||
}
|
||||
);
|
||||
|
||||
match Dotfile::new(manager_path, system_path) {
|
||||
Ok(dotfile) => Some(dotfile),
|
||||
Err(e) => {
|
||||
println!("Failed to read dotfile: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
Dotfile::new(manager_path, system_path)
|
||||
});
|
||||
|
||||
let valid_dotfiles: Vec<Dotfile> = dotfiles.filter_map(|dotfile| match dotfile {
|
||||
Some(dotfile) => Some(dotfile),
|
||||
None => {
|
||||
println!("Failed to parse config");
|
||||
None
|
||||
},
|
||||
}).collect();
|
||||
Ok(dotfiles.collect())
|
||||
|
||||
let manager_dir = if config_file.contains_key("manager_directory") {
|
||||
PathBuf::from(config_file.get("manager_directory").unwrap().as_str().unwrap())
|
||||
}
|
||||
|
||||
|
||||
fn get_manager_dir(config: &Table) -> PathBuf {
|
||||
|
||||
let manager_dir = if config.contains_key("manager_directory") {
|
||||
PathBuf::from(config.get("manager_directory").unwrap().as_str().unwrap())
|
||||
} else {
|
||||
PathBuf::from("$HOME/.dotfiles")
|
||||
};
|
||||
|
||||
Ok(Config{manager_dir, dotfiles: valid_dotfiles})
|
||||
manager_dir
|
||||
}
|
||||
|
||||
|
||||
fn produce_error(code: usize) -> Box<dyn Error> {
|
||||
let error = match code {
|
||||
1 => ConfigParseError {
|
||||
code: 1,
|
||||
message: String::from("No dotfiles section in config"),
|
||||
},
|
||||
2 => ConfigParseError {
|
||||
code: 2,
|
||||
message: String::from("Dotfiles is not a valid config"),
|
||||
},
|
||||
3 => ConfigParseError {
|
||||
code: 3,
|
||||
message: String::from("A dotfile section in config is not valid"),
|
||||
},
|
||||
_ => ConfigParseError {
|
||||
code: 99,
|
||||
message: String::from("Error parsing config"),
|
||||
}
|
||||
};
|
||||
|
||||
Box::new(error)
|
||||
}
|
||||
}
|
||||
|
||||
struct ConfigParseError {
|
||||
code: usize,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl Error for ConfigParseError {}
|
||||
|
||||
impl fmt::Display for ConfigParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let err = match self.code{
|
||||
1 => "No dotfiles section in config",
|
||||
2 => "Dotfiles section in config is not a valid array, Hint: Use [[dotfiles]]",
|
||||
3 => "A dotfile section in config is not valid",
|
||||
_ => "Error parsing config",
|
||||
};
|
||||
|
||||
write!(f, "{}", err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ConfigParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ConfigReadError {{ code: {}, message: {} }}",
|
||||
self.code, self.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
17
src/lib.rs
17
src/lib.rs
@@ -47,16 +47,21 @@ fn copy_directory(dir_path: &PathBuf, dest_path: &PathBuf) -> Result<(), Box<dyn
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pub fn run(args: ArgMatches, config: Config) -> Result<(), Box<dyn Error>> {
|
||||
|
||||
let dotfiles = config.dotfiles;
|
||||
|
||||
let copy_to_sys = args.get_flag("from-git");
|
||||
|
||||
let copy_results = dotfiles.iter().map(|dotfile| (dotfile.copy_dotfile(copy_to_sys), dotfile));
|
||||
let dotfiles = config.dotfiles;
|
||||
|
||||
let valid_dotfiles: Vec<_> = dotfiles.iter().filter_map(|dotfile| match dotfile {
|
||||
Ok(dotfile) => Some(dotfile),
|
||||
Err(e) => {
|
||||
println!("Failed to read a dotfile: {}", e);
|
||||
None
|
||||
},
|
||||
}).collect();
|
||||
|
||||
let copy_results = valid_dotfiles.iter().map(|dotfile| (dotfile.copy_dotfile(copy_to_sys), dotfile));
|
||||
|
||||
copy_results.for_each(|result| {
|
||||
if let Err(e) = result.0 {
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
use std::path::PathBuf;
|
||||
use std::error::Error;
|
||||
|
||||
use dotfiles_manager::args;
|
||||
use dotfiles_manager::config::Config;
|
||||
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args = args::parse_args();
|
||||
|
||||
let program_config = Config::parse(PathBuf::from("/home/eesim/.config/dotfiles/config"));
|
||||
let program_config = Config::parse(PathBuf::from("/home/eesim/.config/dotfiles/config"))?;
|
||||
|
||||
if let Err(e) = dotfiles_manager::run(args, program_config.unwrap()) {
|
||||
if let Err(e) = dotfiles_manager::run(args, program_config) {
|
||||
panic!("Error: {}", e)
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user