Add config file as cli option (#576)
* Parse cli arguments before configuration file Parsing the cli arguments before the configuration file allows `--help` and `--version` to be used even if the configuration file is broken. * Add configuration file to command line arguments This commit adds a new command line flag `--config-file` to override the default configuration file location. If the specified file is unavailable, Alacritty will quit instead of generating a fallback. If the specified file is invalid, i.e. /dev/null, the compiled in defaults will be loaded instead.
This commit is contained in:
parent
13eac6b673
commit
81116fb8a4
17
src/cli.rs
17
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<Shell<'static>>,
|
||||
pub working_dir: Option<PathBuf>,
|
||||
pub config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
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<Cow<Path>> {
|
||||
self.config.as_ref().map(|p| Cow::Borrowed(p.as_path()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<serde_yaml::Error> for Error {
|
|||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
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<Config> {
|
||||
let home = env::var("HOME")?;
|
||||
|
||||
pub fn installed_config() -> Option<Cow<'static, Path>> {
|
||||
// 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<PathBuf> {
|
||||
pub fn write_defaults() -> io::Result<Cow<'static, Path>> {
|
||||
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<P: Into<PathBuf>>(path: P) -> Result<Config> {
|
||||
pub fn load_from<P: Into<PathBuf>>(path: P) -> Result<Config> {
|
||||
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();
|
||||
|
|
49
src/main.rs
49
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
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue