aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/config/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/config/mod.rs')
-rw-r--r--alacritty/src/config/mod.rs109
1 files changed, 82 insertions, 27 deletions
diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs
index 226c6775..e5cc8583 100644
--- a/alacritty/src/config/mod.rs
+++ b/alacritty/src/config/mod.rs
@@ -1,26 +1,32 @@
-use std::env;
use std::fmt::{self, Display, Formatter};
-use std::fs;
-use std::io;
use std::path::PathBuf;
+use std::{env, fs, io};
use log::{error, warn};
+use serde::Deserialize;
+use serde_yaml::mapping::Mapping;
+use serde_yaml::Value;
use alacritty_terminal::config::{Config as TermConfig, LOG_TARGET_CONFIG};
-mod bindings;
pub mod debug;
pub mod font;
pub mod monitor;
-mod mouse;
pub mod ui_config;
pub mod window;
+mod bindings;
+mod mouse;
+mod serde_utils;
+
pub use crate::config::bindings::{Action, Binding, Key, ViAction};
#[cfg(test)]
pub use crate::config::mouse::{ClickHandler, Mouse};
use crate::config::ui_config::UIConfig;
+/// Maximum number of depth for the configuration file imports.
+const IMPORT_RECURSION_LIMIT: usize = 5;
+
pub type Config = TermConfig<UIConfig>;
/// Result from config loading.
@@ -128,13 +134,7 @@ pub fn installed_config() -> Option<PathBuf> {
dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists())
}
-pub fn load_from(path: PathBuf) -> Config {
- let mut config = reload_from(&path).unwrap_or_else(|_| Config::default());
- config.config_path = Some(path);
- config
-}
-
-pub fn reload_from(path: &PathBuf) -> Result<Config> {
+pub fn load_from(path: &PathBuf) -> Result<Config> {
match read_config(path) {
Ok(config) => Ok(config),
Err(err) => {
@@ -145,6 +145,25 @@ pub fn reload_from(path: &PathBuf) -> Result<Config> {
}
fn read_config(path: &PathBuf) -> Result<Config> {
+ let mut config_paths = Vec::new();
+ let config_value = parse_config(&path, &mut config_paths, IMPORT_RECURSION_LIMIT)?;
+
+ let mut config = Config::deserialize(config_value)?;
+ config.ui_config.config_paths = config_paths;
+
+ print_deprecation_warnings(&config);
+
+ Ok(config)
+}
+
+/// Deserialize all configuration files as generic Value.
+fn parse_config(
+ path: &PathBuf,
+ config_paths: &mut Vec<PathBuf>,
+ recursion_limit: usize,
+) -> Result<Value> {
+ config_paths.push(path.to_owned());
+
let mut contents = fs::read_to_string(path)?;
// Remove UTF-8 BOM.
@@ -152,24 +171,57 @@ fn read_config(path: &PathBuf) -> Result<Config> {
contents = contents.split_off(3);
}
- parse_config(&contents)
-}
-
-fn parse_config(contents: &str) -> Result<Config> {
- match serde_yaml::from_str(contents) {
+ // Load configuration file as Value.
+ let config: Value = match serde_yaml::from_str(&contents) {
+ Ok(config) => config,
Err(error) => {
// Prevent parsing error with an empty string and commented out file.
if error.to_string() == "EOF while parsing a value" {
- Ok(Config::default())
+ Value::Mapping(Mapping::new())
} else {
- Err(Error::Yaml(error))
+ return Err(Error::Yaml(error));
}
},
- Ok(config) => {
- print_deprecation_warnings(&config);
- Ok(config)
- },
+ };
+
+ // Merge config with imports.
+ let imports = load_imports(&config, config_paths, recursion_limit);
+ Ok(serde_utils::merge(imports, config))
+}
+
+/// Load all referenced configuration files.
+fn load_imports(config: &Value, config_paths: &mut Vec<PathBuf>, recursion_limit: usize) -> Value {
+ let mut merged = Value::Null;
+
+ let imports = match config.get("import") {
+ Some(Value::Sequence(imports)) => imports,
+ _ => return merged,
+ };
+
+ // Limit recursion to prevent infinite loops.
+ if !imports.is_empty() && recursion_limit == 0 {
+ error!(target: LOG_TARGET_CONFIG, "Exceeded maximum configuration import depth");
+ return merged;
}
+
+ for import in imports {
+ let path = match import {
+ Value::String(path) => PathBuf::from(path),
+ _ => {
+ error!(target: LOG_TARGET_CONFIG, "Encountered invalid configuration file import");
+ continue;
+ },
+ };
+
+ match parse_config(&path, config_paths, recursion_limit - 1) {
+ Ok(config) => merged = serde_utils::merge(merged, config),
+ Err(err) => {
+ error!(target: LOG_TARGET_CONFIG, "Unable to import config {:?}: {}", path, err)
+ },
+ }
+ }
+
+ merged
}
fn print_deprecation_warnings(config: &Config) {
@@ -215,13 +267,16 @@ fn print_deprecation_warnings(config: &Config) {
#[cfg(test)]
mod tests {
- static DEFAULT_ALACRITTY_CONFIG: &str =
- include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml"));
+ use super::*;
- use super::Config;
+ static DEFAULT_ALACRITTY_CONFIG: &str =
+ concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml");
#[test]
fn config_read_eof() {
- assert_eq!(super::parse_config(DEFAULT_ALACRITTY_CONFIG).unwrap(), Config::default());
+ let config_path: PathBuf = DEFAULT_ALACRITTY_CONFIG.into();
+ let mut config = read_config(&config_path).unwrap();
+ config.ui_config.config_paths = Vec::new();
+ assert_eq!(config, Config::default());
}
}