Reworked config parsing and added error handling
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::str;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use toml::Table;
|
use toml::Table;
|
||||||
|
|
||||||
use crate::dotfile::Dotfile;
|
use crate::dotfile::Dotfile;
|
||||||
@@ -9,60 +10,139 @@ use crate::dotfile::Dotfile;
|
|||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub manager_dir: PathBuf,
|
pub manager_dir: PathBuf,
|
||||||
pub dotfiles: Vec<Dotfile>,
|
pub dotfiles: Vec<Result<Dotfile, Box<dyn Error>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Config {
|
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 dotfile_table = dotfile.as_table().unwrap();
|
||||||
|
|
||||||
let manager_path = PathBuf::from(
|
let manager_path = PathBuf::from(
|
||||||
match dotfile_table.get("manager_path") {
|
match dotfile_table.get("manager_path") {
|
||||||
Some(path) => path.as_str().expect("Invalid character in dotfile 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(
|
let system_path = PathBuf::from(
|
||||||
match dotfile_table.get("system_path") {
|
match dotfile_table.get("system_path") {
|
||||||
Some(path) => path.as_str().expect("Invalid character in dotfile 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) {
|
Dotfile::new(manager_path, system_path)
|
||||||
Ok(dotfile) => Some(dotfile),
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to read dotfile: {}", e);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let valid_dotfiles: Vec<Dotfile> = dotfiles.filter_map(|dotfile| match dotfile {
|
Ok(dotfiles.collect())
|
||||||
Some(dotfile) => Some(dotfile),
|
|
||||||
None => {
|
|
||||||
println!("Failed to parse config");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}).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 {
|
} else {
|
||||||
PathBuf::from("$HOME/.dotfiles")
|
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>> {
|
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_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| {
|
copy_results.for_each(|result| {
|
||||||
if let Err(e) = result.0 {
|
if let Err(e) = result.0 {
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use dotfiles_manager::args;
|
use dotfiles_manager::args;
|
||||||
use dotfiles_manager::config::Config;
|
use dotfiles_manager::config::Config;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let args = args::parse_args();
|
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)
|
panic!("Error: {}", e)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user