diff --git a/src/cli.rs b/src/cli.rs index 4a455cf4..e7783807 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -15,7 +15,8 @@ extern crate log; use clap::{Arg, App}; use index::{Line, Column}; use config::{Dimensions, Shell}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::borrow::Cow; const DEFAULT_TITLE: &'static str = "Alacritty"; @@ -28,6 +29,7 @@ pub struct Options { pub log_level: log::LogLevelFilter, pub command: Option>, pub working_dir: Option, + pub config: Option, } impl Default for Options { @@ -40,6 +42,7 @@ impl Default for Options { log_level: log::LogLevelFilter::Warn, command: None, working_dir: None, + config: None, } } } @@ -82,6 +85,10 @@ impl Options { .long("working-directory") .takes_value(true) .help("Start the shell in the specified working directory")) + .arg(Arg::with_name("config-file") + .long("config-file") + .takes_value(true) + .help("Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]")) .arg(Arg::with_name("command") .short("e") .multiple(true) @@ -128,6 +135,10 @@ impl Options { options.working_dir = Some(PathBuf::from(dir.to_string())); } + if let Some(path) = matches.value_of("config-file") { + options.config = Some(PathBuf::from(path.to_string())); + } + if let Some(mut args) = matches.values_of("command") { // The following unwrap is guaranteed to succeed. // If 'command' exists it must also have a first item since @@ -147,4 +158,8 @@ impl Options { pub fn command(&self) -> Option<&Shell> { self.command.as_ref() } + + pub fn config_path(&self) -> Option> { + self.config.as_ref().map(|p| Cow::Borrowed(p.as_path())) + } } diff --git a/src/config.rs b/src/config.rs index ada6f8d1..5142b9b0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -721,6 +721,9 @@ pub enum Error { /// Config file not found NotFound, + /// Config file empty + Empty, + /// Couldn't read $HOME environment variable ReadingEnvHome(env::VarError), @@ -923,6 +926,7 @@ impl ::std::error::Error for Error { fn cause(&self) -> Option<&::std::error::Error> { match *self { Error::NotFound => None, + Error::Empty => None, Error::ReadingEnvHome(ref err) => Some(err), Error::Io(ref err) => Some(err), Error::Yaml(ref err) => Some(err), @@ -932,6 +936,7 @@ impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { Error::NotFound => "could not locate config file", + Error::Empty => "empty config file", Error::ReadingEnvHome(ref err) => err.description(), Error::Io(ref err) => err.description(), Error::Yaml(ref err) => err.description(), @@ -943,6 +948,7 @@ impl ::std::fmt::Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { Error::NotFound => write!(f, "{}", ::std::error::Error::description(self)), + Error::Empty => write!(f, "{}", ::std::error::Error::description(self)), Error::ReadingEnvHome(ref err) => { write!(f, "could not read $HOME environment variable: {}", err) }, @@ -978,19 +984,16 @@ impl From for Error { pub type Result = ::std::result::Result; impl Config { - /// Attempt to load the config file - /// - /// The config file is loaded from the first file it finds in this list of paths + /// Get the location of the first found default config file paths + /// according to the following order: /// /// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml /// 2. $XDG_CONFIG_HOME/alacritty.yml /// 3. $HOME/.config/alacritty/alacritty.yml /// 4. $HOME/.alacritty.yml - pub fn load() -> Result { - let home = env::var("HOME")?; - + pub fn installed_config() -> Option> { // Try using XDG location by default - let path = ::xdg::BaseDirectories::with_prefix("alacritty") + ::xdg::BaseDirectories::with_prefix("alacritty") .ok() .and_then(|xdg| xdg.find_config_file("alacritty.yml")) .or_else(|| { @@ -999,27 +1002,29 @@ impl Config { }) }) .or_else(|| { - // Fallback path: $HOME/.config/alacritty/alacritty.yml - let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml"); - match fallback.exists() { - true => Some(fallback), - false => None + if let Ok(home) = env::var("HOME") { + // Fallback path: $HOME/.config/alacritty/alacritty.yml + let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml"); + if fallback.exists() { + return Some(fallback); + } + // Fallback path: $HOME/.alacritty.yml + let fallback = PathBuf::from(&home).join(".alacritty.yml"); + if fallback.exists() { + return Some(fallback); + } } + None }) - .unwrap_or_else(|| { - // Fallback path: $HOME/.alacritty.yml - PathBuf::from(&home).join(".alacritty.yml") - }); - - Config::load_from(path) + .map(|path| path.into()) } - pub fn write_defaults() -> io::Result { + pub fn write_defaults() -> io::Result> { let path = ::xdg::BaseDirectories::with_prefix("alacritty") .map_err(|err| io::Error::new(io::ErrorKind::NotFound, ::std::error::Error::description(&err))) .and_then(|p| p.place_config_file("alacritty.yml"))?; File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?; - Ok(path) + Ok(path.into()) } /// Get list of colors @@ -1116,7 +1121,7 @@ impl Config { self.hide_cursor_when_typing } - fn load_from>(path: P) -> Result { + pub fn load_from>(path: P) -> Result { let path = path.into(); let raw = Config::read_file(path.as_path())?; let mut config: Config = serde_yaml::from_str(&raw)?; @@ -1129,6 +1134,9 @@ impl Config { let mut f = fs::File::open(path)?; let mut contents = String::new(); f.read_to_string(&mut contents)?; + if contents.len() == 0 { + return Err(Error::Empty); + } Ok(contents) } @@ -1427,7 +1435,7 @@ impl Monitor { // Reload file path.map(|path| { if path == config_path { - match Config::load() { + match Config::load_from(path) { Ok(config) => { let _ = config_tx.send(config); handler.on_config_reload(); diff --git a/src/main.rs b/src/main.rs index b7db4dec..f4ee72b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,28 +37,9 @@ use alacritty::tty::{self, process_should_exit}; use alacritty::util::fmt::Red; fn main() { - - // Load configuration - let config = Config::load().unwrap_or_else(|err| { - match err { - // Use default config when not found - config::Error::NotFound => { - match Config::write_defaults() { - Ok(path) => err_println!("Config file not found; write defaults config to {:?}", path), - Err(err) => err_println!("Write defaults config failure: {}", err) - } - - Config::load().unwrap() - }, - - // If there's a problem with the config file, print an error - // and exit. - _ => die!("{}", err), - } - }); - - // Load command line options + // Load command line options and config let options = cli::Options::load(); + let config = load_config(&options); // Run alacritty if let Err(err) = run(config, options) { @@ -68,6 +49,32 @@ fn main() { info!("Goodbye."); } +/// Load configuration +/// +/// If a configuration file is given as a command line argument we don't +/// generate a default file. If an empty configuration file is given, i.e. +/// /dev/null, we load the compiled-in defaults. +fn load_config(options: &cli::Options) -> Config { + let config_path = options.config_path() + .or_else(|| Config::installed_config()) + .unwrap_or_else(|| { + Config::write_defaults() + .unwrap_or_else(|err| die!("Write defaults config failure: {}", err)) + }); + + Config::load_from(&*config_path).unwrap_or_else(|err| { + match err { + config::Error::NotFound => { + die!("Config file not found at: {}", config_path.display()); + }, + config::Error::Empty => { + err_println!("Empty config; Loading defaults"); + Config::default() + }, + _ => die!("{}", err), + } + }) +} /// Run Alacritty ///