extern crate serde; extern crate serde_yaml; use std::fs::{File}; use std::path::Path; use std::io::prelude::*; use std::process::exit; use std::env; use self::serde::{Deserialize}; mod config {} // TODO: built-in validator. // For now, ensure any built Config has called validate() #[derive(Debug, PartialEq, Deserialize)] pub struct Config { pub words: Vec, pub choose: usize, pub rows: Option, pub cols: Option } pub fn default_config_file() -> String { let config_folders = [ env::var("XDG_CONFIG_HOME"), env::var("HOME").and_then(|s| Ok(String::from(Path::new(&s).join(".config").to_str().unwrap()))), Ok(String::from("./")) ]; for f in config_folders.iter() { match f { Err(_) => continue, Ok(dir) => { let config_file_location = Path::new(dir).join("fallout-terminal.yaml"); if Path::is_file(&config_file_location) { return config_file_location.into_os_string().into_string().unwrap() } } } } String::from("") } pub fn load_config_file(config_file_path: String) -> Config { let mut config_file = match File::open(config_file_path.as_str()) { Ok(config_file) => config_file, Err(e) => { println!("Could not open {}: {}", config_file_path, e); exit(1); } }; let mut config_str = String::new(); let config: Config; match config_file.read_to_string(&mut config_str) { Ok(_) => { config = match serde_yaml::from_str(&config_str) { Ok(conf) => conf, Err(e) => { println!("Could not parse YAML in {}: {}", config_file_path, e); exit(1); } }; } Err(e) => { println!("Could not read {}: {}", config_file_path, e); exit(1); } }; validate(&config); config } fn validate(config: &Config) { if config.words.is_empty() { panic!("Config file validation error: word list must not be empty"); } let first_len = config.words[0].len(); for s in &config.words[1..] { if s.len() != first_len { panic!("Config file validation error: All words must be the same length [len({}) != len({})]", config.words[0], s); } } if config.choose > config.words.len() { panic!("'choose' value is greater than the number of available words."); } }