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 clap::{Arg, App};
|
||||||
use index::{Line, Column};
|
use index::{Line, Column};
|
||||||
use config::{Dimensions, Shell};
|
use config::{Dimensions, Shell};
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
const DEFAULT_TITLE: &'static str = "Alacritty";
|
const DEFAULT_TITLE: &'static str = "Alacritty";
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ pub struct Options {
|
||||||
pub log_level: log::LogLevelFilter,
|
pub log_level: log::LogLevelFilter,
|
||||||
pub command: Option<Shell<'static>>,
|
pub command: Option<Shell<'static>>,
|
||||||
pub working_dir: Option<PathBuf>,
|
pub working_dir: Option<PathBuf>,
|
||||||
|
pub config: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Options {
|
impl Default for Options {
|
||||||
|
@ -40,6 +42,7 @@ impl Default for Options {
|
||||||
log_level: log::LogLevelFilter::Warn,
|
log_level: log::LogLevelFilter::Warn,
|
||||||
command: None,
|
command: None,
|
||||||
working_dir: None,
|
working_dir: None,
|
||||||
|
config: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +85,10 @@ impl Options {
|
||||||
.long("working-directory")
|
.long("working-directory")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Start the shell in the specified working directory"))
|
.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")
|
.arg(Arg::with_name("command")
|
||||||
.short("e")
|
.short("e")
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
|
@ -128,6 +135,10 @@ impl Options {
|
||||||
options.working_dir = Some(PathBuf::from(dir.to_string()));
|
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") {
|
if let Some(mut args) = matches.values_of("command") {
|
||||||
// The following unwrap is guaranteed to succeed.
|
// The following unwrap is guaranteed to succeed.
|
||||||
// If 'command' exists it must also have a first item since
|
// If 'command' exists it must also have a first item since
|
||||||
|
@ -147,4 +158,8 @@ impl Options {
|
||||||
pub fn command(&self) -> Option<&Shell> {
|
pub fn command(&self) -> Option<&Shell> {
|
||||||
self.command.as_ref()
|
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
|
/// Config file not found
|
||||||
NotFound,
|
NotFound,
|
||||||
|
|
||||||
|
/// Config file empty
|
||||||
|
Empty,
|
||||||
|
|
||||||
/// Couldn't read $HOME environment variable
|
/// Couldn't read $HOME environment variable
|
||||||
ReadingEnvHome(env::VarError),
|
ReadingEnvHome(env::VarError),
|
||||||
|
|
||||||
|
@ -923,6 +926,7 @@ impl ::std::error::Error for Error {
|
||||||
fn cause(&self) -> Option<&::std::error::Error> {
|
fn cause(&self) -> Option<&::std::error::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Error::NotFound => None,
|
Error::NotFound => None,
|
||||||
|
Error::Empty => None,
|
||||||
Error::ReadingEnvHome(ref err) => Some(err),
|
Error::ReadingEnvHome(ref err) => Some(err),
|
||||||
Error::Io(ref err) => Some(err),
|
Error::Io(ref err) => Some(err),
|
||||||
Error::Yaml(ref err) => Some(err),
|
Error::Yaml(ref err) => Some(err),
|
||||||
|
@ -932,6 +936,7 @@ impl ::std::error::Error for Error {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
Error::NotFound => "could not locate config file",
|
Error::NotFound => "could not locate config file",
|
||||||
|
Error::Empty => "empty config file",
|
||||||
Error::ReadingEnvHome(ref err) => err.description(),
|
Error::ReadingEnvHome(ref err) => err.description(),
|
||||||
Error::Io(ref err) => err.description(),
|
Error::Io(ref err) => err.description(),
|
||||||
Error::Yaml(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 {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::NotFound => write!(f, "{}", ::std::error::Error::description(self)),
|
Error::NotFound => write!(f, "{}", ::std::error::Error::description(self)),
|
||||||
|
Error::Empty => write!(f, "{}", ::std::error::Error::description(self)),
|
||||||
Error::ReadingEnvHome(ref err) => {
|
Error::ReadingEnvHome(ref err) => {
|
||||||
write!(f, "could not read $HOME environment variable: {}", 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>;
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Attempt to load the config file
|
/// Get the location of the first found default config file paths
|
||||||
///
|
/// according to the following order:
|
||||||
/// The config file is loaded from the first file it finds in this list of paths
|
|
||||||
///
|
///
|
||||||
/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml
|
/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml
|
||||||
/// 2. $XDG_CONFIG_HOME/alacritty.yml
|
/// 2. $XDG_CONFIG_HOME/alacritty.yml
|
||||||
/// 3. $HOME/.config/alacritty/alacritty.yml
|
/// 3. $HOME/.config/alacritty/alacritty.yml
|
||||||
/// 4. $HOME/.alacritty.yml
|
/// 4. $HOME/.alacritty.yml
|
||||||
pub fn load() -> Result<Config> {
|
pub fn installed_config() -> Option<Cow<'static, Path>> {
|
||||||
let home = env::var("HOME")?;
|
|
||||||
|
|
||||||
// Try using XDG location by default
|
// Try using XDG location by default
|
||||||
let path = ::xdg::BaseDirectories::with_prefix("alacritty")
|
::xdg::BaseDirectories::with_prefix("alacritty")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|xdg| xdg.find_config_file("alacritty.yml"))
|
.and_then(|xdg| xdg.find_config_file("alacritty.yml"))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
|
@ -999,27 +1002,29 @@ impl Config {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
// Fallback path: $HOME/.config/alacritty/alacritty.yml
|
if let Ok(home) = env::var("HOME") {
|
||||||
let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml");
|
// Fallback path: $HOME/.config/alacritty/alacritty.yml
|
||||||
match fallback.exists() {
|
let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml");
|
||||||
true => Some(fallback),
|
if fallback.exists() {
|
||||||
false => None
|
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(|| {
|
.map(|path| path.into())
|
||||||
// Fallback path: $HOME/.alacritty.yml
|
|
||||||
PathBuf::from(&home).join(".alacritty.yml")
|
|
||||||
});
|
|
||||||
|
|
||||||
Config::load_from(path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_defaults() -> io::Result<PathBuf> {
|
pub fn write_defaults() -> io::Result<Cow<'static, Path>> {
|
||||||
let path = ::xdg::BaseDirectories::with_prefix("alacritty")
|
let path = ::xdg::BaseDirectories::with_prefix("alacritty")
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::NotFound, ::std::error::Error::description(&err)))
|
.map_err(|err| io::Error::new(io::ErrorKind::NotFound, ::std::error::Error::description(&err)))
|
||||||
.and_then(|p| p.place_config_file("alacritty.yml"))?;
|
.and_then(|p| p.place_config_file("alacritty.yml"))?;
|
||||||
File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?;
|
File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?;
|
||||||
Ok(path)
|
Ok(path.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of colors
|
/// Get list of colors
|
||||||
|
@ -1116,7 +1121,7 @@ impl Config {
|
||||||
self.hide_cursor_when_typing
|
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 path = path.into();
|
||||||
let raw = Config::read_file(path.as_path())?;
|
let raw = Config::read_file(path.as_path())?;
|
||||||
let mut config: Config = serde_yaml::from_str(&raw)?;
|
let mut config: Config = serde_yaml::from_str(&raw)?;
|
||||||
|
@ -1129,6 +1134,9 @@ impl Config {
|
||||||
let mut f = fs::File::open(path)?;
|
let mut f = fs::File::open(path)?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
f.read_to_string(&mut contents)?;
|
f.read_to_string(&mut contents)?;
|
||||||
|
if contents.len() == 0 {
|
||||||
|
return Err(Error::Empty);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(contents)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
|
@ -1427,7 +1435,7 @@ impl Monitor {
|
||||||
// Reload file
|
// Reload file
|
||||||
path.map(|path| {
|
path.map(|path| {
|
||||||
if path == config_path {
|
if path == config_path {
|
||||||
match Config::load() {
|
match Config::load_from(path) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
let _ = config_tx.send(config);
|
let _ = config_tx.send(config);
|
||||||
handler.on_config_reload();
|
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;
|
use alacritty::util::fmt::Red;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// Load command line options and config
|
||||||
// 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
|
|
||||||
let options = cli::Options::load();
|
let options = cli::Options::load();
|
||||||
|
let config = load_config(&options);
|
||||||
|
|
||||||
// Run alacritty
|
// Run alacritty
|
||||||
if let Err(err) = run(config, options) {
|
if let Err(err) = run(config, options) {
|
||||||
|
@ -68,6 +49,32 @@ fn main() {
|
||||||
info!("Goodbye.");
|
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
|
/// Run Alacritty
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in New Issue