diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..8493097 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,201 @@ +use clap::Parser; +/// Defines the arguments passed to the program. +/// +/// # Examples +/// +/// ``` +/// use clap::Parser; +/// +/// let args = Args::parse(); +/// assert_eq!("config.json", args.config_file_path); +/// ``` +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct Args { + /// The file path to the config file + #[arg(short, long, default_value = "config.json")] + pub config_file_path: String, + /// The path to the directory to do work in + #[arg(short, long, default_value = "~/dotfiles")] + pub destination_dir_path: String, + /// Whether to treat the user's home folder as their username or "$USER" + #[arg(short, long, default_value_t = false)] + pub use_username: bool, +} +/// The root of a Linux System +const ROOT: &str = "/"; +/// Defines the Configuration for the progam +pub struct Config { + /// The Config as a JSON Object + pub config: serde_json::Value, + /// The paths currently stored for this configuration + pub paths: Vec, + /// The root path of all work done + pub root: String, + /// Whether to treat the user's home folder as their username or "$USER" + pub use_username: bool, + /// The path to the directory to do work in + pub destination_path: String, +} +impl Config { + /// Returns a new instance of Config + /// + /// # Arguments + /// + /// * `config_file_path` - The file path to the config file + /// + /// # Examples + /// + /// ``` + /// let config = Config::new("config.json"); + /// assert_eq!(ROOT, config.root); + /// ``` + pub fn new(args: Args) -> Self { + let root = "/"; + let paths = vec![root.to_string()]; + let config = parse_config(&args.config_file_path); + + Self { + paths, + config, + root: root.to_string(), + use_username: args.use_username, + destination_path: args.destination_dir_path, + } + } + /// Initializes the Config instance + /// + /// # Examples + /// + /// ``` + /// let config = Config::new("config.json"); + /// config.run(); + /// assert!(config.paths.len() > 1); + /// ``` + pub fn run(&mut self) { + let mut config_root = self.root.clone(); + let mut config_tree = self.config.clone(); + + self.generate_paths(&mut config_tree, &mut config_root) + } + /// Recursively generates file paths from a JSON Object + /// + /// # Arguments + /// + /// * `json_file_tree` - The file_tree of paths to generate as a JSON Object + /// * `current_root` - The current operating directory path + fn generate_paths(&mut self, json_file_tree: &serde_json::Value, current_root: &mut String) { + match json_file_tree { + serde_json::Value::Object(dir) => self.parse_dir(current_root.clone() ,dir), + serde_json::Value::Array(dir_list) => self.parse_list(dir_list, current_root), + serde_json::Value::String(file) => self.parse_file(file, current_root.clone()), + _ => (), + } + } + /// Parses a directory from a JSON Object + /// + /// # Arguments + /// + /// * `current_root` - The current operating directory path + /// * `dir` - The JSON Object to be parsed as a Map + fn parse_dir(&mut self, mut current_root: String, dir: &serde_json::Map) { + let dirname = match dir.keys().next() { + Some(dirname) => convert_if_user_folder(dirname, self.use_username), + None => "".to_string(), + }; + + current_root.push_str(&dirname); + + current_root.push_str(ROOT); + self.paths.push(current_root.to_string()); + + let json_file_tree = match dir.values().next() { + Some(value) => value, + None => errors::json_parsing_error(), + }; + + self.generate_paths(json_file_tree, &mut current_root) + } + /// Parses a vector of directories from JSON Objects + /// + /// # Arguments + /// + ///* 'dir_list' - The vector to be parsed + /// * `current_root` - The current operating directory path + fn parse_list(&mut self, dir_list: &Vec, current_root: &mut String) { + let mut current_root = current_root.clone(); + dir_list.iter().for_each(|dir| + self.generate_paths(dir, &mut current_root) + ); + } + /// Parses a file from a JSON String + /// + /// # Arguments + /// + /// * `file` - The string to be parsed + /// * `current_root` - The current operating directory path + fn parse_file(&mut self, file: &str, mut current_root: String) { + current_root.push_str(file); + + self.paths.push(current_root.to_string()) + } +} +/// Parses the JSON file at file_path and returns a serde_json::Value. +/// Exits program upon error. +/// +/// # Arguments +/// +/// * `file_path` - The file path of the config to be parsed +/// +/// # Examples +/// +/// ``` +/// let config = parse_config("config.json"); +/// println!("{:?}", config); +/// ``` +fn parse_config(file_path: &str) -> serde_json::Value { + let json_string = read_file_from_path(file_path); + + let value = match serde_json::from_str(&json_string) { + Ok(config) => config, + Err(_) => errors::json_parsing_error(), + }; + + value +} +/// Reads a file to a String. +/// Exits program upon error. +/// +/// # Arguments +/// +/// * `file_path` - The file path of the file to be read +/// +/// # Examples +/// +/// ``` +/// let config = read_file_from_path("config.json"); +/// println!("{}", config); +/// ``` +fn read_file_from_path(file_path: &str) -> String { + use std::fs; + + match fs::read_to_string(file_path) { + Ok(config) => config, + Err(_) => { + eprintln!("Error reading file. Check that the file exists and/or the supplied path is correct."); + + std::process::exit(127) + }, + } +} +fn convert_if_user_folder(dirname: &str, want_username: bool) -> String { + use std::env; + + let env_user = env::var("USER").unwrap_or("\n".to_string()); + + match dirname { + name if !want_username & (name == env_user) => "$USER".to_string(), + "$USER" if want_username => env_user, + _ => dirname.to_string(), + } +} diff --git a/src/lib.rs b/src/lib.rs index a2fa788..9be775a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,206 +1,5 @@ pub mod collect; +pub mod config; pub mod errors; pub mod header; pub mod install; - -use clap::Parser; -/// Defines the arguments passed to the program. -/// -/// # Examples -/// -/// ``` -/// use clap::Parser; -/// -/// let args = Args::parse(); -/// assert_eq!("config.json", args.config_file_path); -/// ``` -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -pub struct Args { - /// The file path to the config file - #[arg(short, long, default_value = "config.json")] - pub config_file_path: String, - /// The path to the directory to do work in - #[arg(short, long, default_value = "~/dotfiles")] - pub destination_dir_path: String, - /// Whether to treat the user's home folder as their username or "$USER" - #[arg(short, long, default_value_t = false)] - pub use_username: bool, -} -/// The root of a Linux System -const ROOT: &str = "/"; -/// Defines the Configuration for the progam -pub struct Config { - /// The Config as a JSON Object - pub config: serde_json::Value, - /// The paths currently stored for this configuration - pub paths: Vec, - /// The root path of all work done - pub root: String, - /// Whether to treat the user's home folder as their username or "$USER" - pub use_username: bool, - /// The path to the directory to do work in - pub destination_path: String, -} -impl Config { - /// Returns a new instance of Config - /// - /// # Arguments - /// - /// * `config_file_path` - The file path to the config file - /// - /// # Examples - /// - /// ``` - /// let config = Config::new("config.json"); - /// assert_eq!(ROOT, config.root); - /// ``` - pub fn new(args: Args) -> Self { - let root = "/"; - let paths = vec![root.to_string()]; - let config = parse_config(&args.config_file_path); - - Self { - paths, - config, - root: root.to_string(), - use_username: args.use_username, - destination_path: args.destination_dir_path, - } - } - /// Initializes the Config instance - /// - /// # Examples - /// - /// ``` - /// let config = Config::new("config.json"); - /// config.run(); - /// assert!(config.paths.len() > 1); - /// ``` - pub fn run(&mut self) { - let mut config_root = self.root.clone(); - let mut config_tree = self.config.clone(); - - self.generate_paths(&mut config_tree, &mut config_root) - } - /// Recursively generates file paths from a JSON Object - /// - /// # Arguments - /// - /// * `json_file_tree` - The file_tree of paths to generate as a JSON Object - /// * `current_root` - The current operating directory path - fn generate_paths(&mut self, json_file_tree: &serde_json::Value, current_root: &mut String) { - match json_file_tree { - serde_json::Value::Object(dir) => self.parse_dir(current_root.clone() ,dir), - serde_json::Value::Array(dir_list) => self.parse_list(dir_list, current_root), - serde_json::Value::String(file) => self.parse_file(file, current_root.clone()), - _ => (), - } - } - /// Parses a directory from a JSON Object - /// - /// # Arguments - /// - /// * `current_root` - The current operating directory path - /// * `dir` - The JSON Object to be parsed as a Map - fn parse_dir(&mut self, mut current_root: String, dir: &serde_json::Map) { - let dirname = match dir.keys().next() { - Some(dirname) => convert_if_user_folder(dirname, self.use_username), - None => "".to_string(), - }; - - current_root.push_str(&dirname); - - current_root.push_str(ROOT); - self.paths.push(current_root.to_string()); - - let json_file_tree = match dir.values().next() { - Some(value) => value, - None => errors::json_parsing_error(), - }; - - self.generate_paths(json_file_tree, &mut current_root) - } - /// Parses a vector of directories from JSON Objects - /// - /// # Arguments - /// - ///* 'dir_list' - The vector to be parsed - /// * `current_root` - The current operating directory path - fn parse_list(&mut self, dir_list: &Vec, current_root: &mut String) { - let mut current_root = current_root.clone(); - dir_list.iter().for_each(|dir| - self.generate_paths(dir, &mut current_root) - ); - } - /// Parses a file from a JSON String - /// - /// # Arguments - /// - /// * `file` - The string to be parsed - /// * `current_root` - The current operating directory path - fn parse_file(&mut self, file: &str, mut current_root: String) { - current_root.push_str(file); - - self.paths.push(current_root.to_string()) - } -} -/// Parses the JSON file at file_path and returns a serde_json::Value. -/// Exits program upon error. -/// -/// # Arguments -/// -/// * `file_path` - The file path of the config to be parsed -/// -/// # Examples -/// -/// ``` -/// let config = parse_config("config.json"); -/// println!("{:?}", config); -/// ``` -pub fn parse_config(file_path: &str) -> serde_json::Value { - let json_string = read_file_from_path(file_path); - - let value = match serde_json::from_str(&json_string) { - Ok(config) => config, - Err(_) => errors::json_parsing_error(), - }; - - value -} -/// Reads a file to a String. -/// Exits program upon error. -/// -/// # Arguments -/// -/// * `file_path` - The file path of the file to be read -/// -/// # Examples -/// -/// ``` -/// let config = read_file_from_path("config.json"); -/// println!("{}", config); -/// ``` -fn read_file_from_path(file_path: &str) -> String { - use std::fs; - - match fs::read_to_string(file_path) { - Ok(config) => config, - Err(_) => { - eprintln!("Error reading file. Check that the file exists and/or the supplied path is correct."); - - std::process::exit(127) - }, - } -} -fn convert_if_user_folder(dirname: &str, want_username: bool) -> String { - use std::env; - - let env_user = env::var("USER").unwrap_or("\n".to_string()); - - match dirname { - name if !want_username & (name == env_user) => "$USER".to_string(), - "$USER" if want_username => env_user, - _ => dirname.to_string(), - } -}