Mercurial > public > mercurial-scm > hg-stable
diff rust/hg-core/src/config/config.rs @ 46542:2845892dd489
rust: Parse system and user configuration
CLI `--config` argument parsing is still missing, as is per-repo config
Differential Revision: https://phab.mercurial-scm.org/D9961
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Thu, 04 Feb 2021 13:16:21 +0100 |
parents | 0d734c0ae1cf |
children | d7685105e504 |
line wrap: on
line diff
--- a/rust/hg-core/src/config/config.rs Mon Feb 01 13:32:00 2021 +0100 +++ b/rust/hg-core/src/config/config.rs Thu Feb 04 13:16:21 2021 +0100 @@ -11,8 +11,11 @@ use crate::config::layer::{ ConfigError, ConfigLayer, ConfigParseError, ConfigValue, }; -use std::path::PathBuf; +use crate::utils::files::get_bytes_from_path; +use std::env; +use std::path::{Path, PathBuf}; +use crate::errors::{HgResultExt, IoResultExt}; use crate::repo::Repo; /// Holds the config values for the current repository @@ -50,6 +53,124 @@ } impl Config { + /// Load system and user configuration from various files. + /// + /// This is also affected by some environment variables. + /// + /// TODO: add a parameter for `--config` CLI arguments + pub fn load() -> Result<Self, ConfigError> { + let mut config = Self { layers: Vec::new() }; + let opt_rc_path = env::var_os("HGRCPATH"); + // HGRCPATH replaces system config + if opt_rc_path.is_none() { + config.add_system_config()? + } + config.add_for_environment_variable("EDITOR", b"ui", b"editor"); + config.add_for_environment_variable("VISUAL", b"ui", b"editor"); + config.add_for_environment_variable("PAGER", b"pager", b"pager"); + // HGRCPATH replaces user config + if opt_rc_path.is_none() { + config.add_user_config()? + } + if let Some(rc_path) = &opt_rc_path { + for path in env::split_paths(rc_path) { + if !path.as_os_str().is_empty() { + if path.is_dir() { + config.add_trusted_dir(&path)? + } else { + config.add_trusted_file(&path)? + } + } + } + } + Ok(config) + } + + fn add_trusted_dir(&mut self, path: &Path) -> Result<(), ConfigError> { + if let Some(entries) = std::fs::read_dir(path) + .for_file(path) + .io_not_found_as_none()? + { + for entry in entries { + let file_path = entry.for_file(path)?.path(); + if file_path.extension() == Some(std::ffi::OsStr::new("rc")) { + self.add_trusted_file(&file_path)? + } + } + } + Ok(()) + } + + fn add_trusted_file(&mut self, path: &Path) -> Result<(), ConfigError> { + if let Some(data) = + std::fs::read(path).for_file(path).io_not_found_as_none()? + { + self.layers.extend(ConfigLayer::parse(path, &data)?) + } + Ok(()) + } + + fn add_for_environment_variable( + &mut self, + var: &str, + section: &[u8], + key: &[u8], + ) { + if let Some(value) = env::var_os(var) { + let origin = layer::ConfigOrigin::Environment(var.into()); + let mut layer = ConfigLayer::new(origin); + layer.add( + section.to_owned(), + key.to_owned(), + // `value` is not a path but this works for any `OsStr`: + get_bytes_from_path(value), + None, + ); + self.layers.push(layer) + } + } + + #[cfg(unix)] // TODO: other platforms + fn add_system_config(&mut self) -> Result<(), ConfigError> { + let mut add_for_prefix = |prefix: &Path| -> Result<(), ConfigError> { + let etc = prefix.join("etc").join("mercurial"); + self.add_trusted_file(&etc.join("hgrc"))?; + self.add_trusted_dir(&etc.join("hgrc.d")) + }; + let root = Path::new("/"); + // TODO: use `std::env::args_os().next().unwrap()` a.k.a. argv[0] + // instead? TODO: can this be a relative path? + let hg = crate::utils::current_exe()?; + // TODO: this order (per-installation then per-system) matches + // `systemrcpath()` in `mercurial/scmposix.py`, but + // `mercurial/helptext/config.txt` suggests it should be reversed + if let Some(installation_prefix) = hg.parent().and_then(Path::parent) { + if installation_prefix != root { + add_for_prefix(&installation_prefix)? + } + } + add_for_prefix(root)?; + Ok(()) + } + + #[cfg(unix)] // TODO: other plateforms + fn add_user_config(&mut self) -> Result<(), ConfigError> { + let opt_home = home::home_dir(); + if let Some(home) = &opt_home { + self.add_trusted_file(&home.join(".hgrc"))? + } + let darwin = cfg!(any(target_os = "macos", target_os = "ios")); + if !darwin { + if let Some(config_home) = env::var_os("XDG_CONFIG_HOME") + .map(PathBuf::from) + .or_else(|| opt_home.map(|home| home.join(".config"))) + { + self.add_trusted_file(&config_home.join("hg").join("hgrc"))? + } + } + Ok(()) + } + /// Loads in order, which means that the precedence is the same /// as the order of `sources`. pub fn load_from_explicit_sources(