use crate::common; use core::fmt; use std::io::Read; use std::path::PathBuf; use std::fs; use std::env; use std::fs::{File, Metadata}; use toml; use serde_derive::Deserialize; pub struct ConfigFile { pub path: String, pub file: Metadata, pub config: Config } impl fmt::Display for ConfigFile { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let branches_list = match &self.config.branches { None => String::from("All branches"), Some(vec) => { let mut out = String::from("["); for i in 0..vec.len() { out = format!("{}{}", out, vec[i]); if i < vec.len() - 1 { out.push_str(", "); } } out.push(']'); out } }; let mut to_list = String::from("["); for i in 0..self.config.to.len() { to_list.push_str(&self.config.to[i]); if i < self.config.to.len() - 1 { to_list.push_str(", "); } }; to_list.push(']'); let work_dir_path = match &self.config.work_dir { None => { if cfg!(windows) { format!("Using default \"{}\\refractr\"", match env::var("TEMP") { Ok(val) => val, Err(_) => format!("This shouldn't happen!") }) } else { format!("Using default: /tmp/refractr") } }, Some(path) => format!("{}", path) }; let schedule_interval = match self.config.schedule.interval { None => { if !self.config.schedule.enabled { String::from("") } else { String::from("This shouldn't happen!\n") } }, Some(int) => format!("\n Scheduled interval in seconds: {}", int.to_string()) }; write!(f, "Config file: \"{}\"\n \ Is a file: {}\n \ Read only: {}\n \ Configuration:\n \ Git repo to clone: {}\n \ Git repos to push clone to: {}\n \ Branches of clone to push: {}\n \ Working directory: {}\n \ SSH key for pushing clone: {}\n \ Schedule enabled: {}\ {}" , self.path, self.file.is_file(), self.file.permissions().readonly(), self.config.from , to_list, branches_list, work_dir_path, self.config.git.ssh_identity_file, self.config.schedule.enabled , schedule_interval) } } #[derive(PartialEq)] #[derive(Deserialize)] pub struct Config { pub from: String, pub to: Vec, pub branches: Option>, pub work_dir: Option, pub git: Git, pub schedule: Schedule } #[derive(PartialEq)] #[derive(Deserialize)] pub struct Git { pub ssh_identity_file: String, } #[derive(PartialEq)] #[derive(Deserialize)] pub struct Schedule { pub enabled: bool, interval: Option, } pub fn read_config(paths: Vec, refractr: &common::Refractr) -> Vec { let mut config_files: Vec = vec![]; for path in paths { common::verbose(refractr.verbose, 1, format!("Reading config file: \"{}\"", String::from(path.to_string_lossy()))); let mut data = String::new(); let mut file = match File::open(path.as_path()) { Err(e) => panic!("refractr: unable to open {}: {}", path.as_path().display(), e), Ok(file) => file }; if let Err(e) = file.read_to_string(&mut data) { panic!("refractr: unable to read {}: {}", path.as_path().display(), e) } let config_file = ConfigFile { path: match fs::canonicalize(&path) { Err(_) => panic!("refractr: cannot get absolute path of config file: {}", path.as_path().display()), Ok(abs) => abs.to_string_lossy().to_string() }, file: match fs::metadata(&path) { Err(_) => panic!("refractr: cannot obtain metadata for config file: {}", path.as_path().display()), Ok(metadata) => metadata }, config: verify_config(toml::from_str(&data).unwrap()) }; let mut dup = false; for i in &config_files { if i.path == config_file.path { eprintln!("refractr: warning: skipping config file \"{}\" as it was already read", path.as_path().display()); dup = true; break; } else if i.config == config_file.config { eprintln!("refractr: warning: config files \"{}\" and \"{}\" appear to have the same config", i.path, config_file.path); } } if !dup { config_files.push(config_file); } } return config_files; } fn verify_config(config: Config) -> Config { if config.schedule.enabled { assert_ne!(config.schedule.interval, None); } assert_ne!("", match &config.work_dir { Some(path) => format!("{}", path), None => { if cfg!(windows) { match env::var("TEMP") { Ok(val) => val, Err(_) => format!("") } } else { format!("/tmp/refractr") } }, }); return config; }