# HG changeset patch # User Simon Sapin # Date 1612824118 -3600 # Node ID a25033eb43b50ccd4947c376894cfb5a9529a861 # Parent 2e5dd18d6dc3ee12a9e4098da5b4c8c732cf171e rhg: add limited support for the `config` sub-command Only with one argument and no flag. This is mostly for testing. Differential Revision: https://phab.mercurial-scm.org/D9972 diff -r 2e5dd18d6dc3 -r a25033eb43b5 rust/hg-core/src/config/layer.rs --- a/rust/hg-core/src/config/layer.rs Mon Feb 08 23:08:44 2021 +0100 +++ b/rust/hg-core/src/config/layer.rs Mon Feb 08 23:41:58 2021 +0100 @@ -58,8 +58,8 @@ fn parse_one(arg: &[u8]) -> Option<(Vec, Vec, Vec)> { use crate::utils::SliceExt; - let (section_and_item, value) = split_2(arg, b'=')?; - let (section, item) = split_2(section_and_item.trim(), b'.')?; + let (section_and_item, value) = arg.split_2(b'=')?; + let (section, item) = section_and_item.trim().split_2(b'.')?; Some(( section.to_owned(), item.to_owned(), @@ -67,13 +67,6 @@ )) } - fn split_2(bytes: &[u8], separator: u8) -> Option<(&[u8], &[u8])> { - let mut iter = bytes.splitn(2, |&byte| byte == separator); - let a = iter.next()?; - let b = iter.next()?; - Some((a, b)) - } - let mut layer = Self::new(ConfigOrigin::CommandLine); for arg in cli_config_args { let arg = arg.as_ref(); diff -r 2e5dd18d6dc3 -r a25033eb43b5 rust/hg-core/src/repo.rs --- a/rust/hg-core/src/repo.rs Mon Feb 08 23:08:44 2021 +0100 +++ b/rust/hg-core/src/repo.rs Mon Feb 08 23:41:58 2021 +0100 @@ -43,10 +43,14 @@ } impl Repo { - /// Search the current directory and its ancestores for a repository: - /// a working directory that contains a `.hg` sub-directory. + /// Find a repository, either at the given path (which must contain a `.hg` + /// sub-directory) or by searching the current directory and its + /// ancestors. /// - /// `explicit_path` is for `--repository` command-line arguments. + /// A method with two very different "modes" like this usually a code smell + /// to make two methods instead, but in this case an `Option` is what rhg + /// sub-commands get from Clap for the `-R` / `--repository` CLI argument. + /// Having two methods would just move that `if` to almost all callers. pub fn find( config: &Config, explicit_path: Option<&Path>, @@ -77,6 +81,28 @@ } } + /// Like `Repo::find`, but not finding a repository is not an error if no + /// explicit path is given. `Ok(None)` is returned in that case. + /// + /// If an explicit path *is* given, not finding a repository there is still + /// an error. + /// + /// For sub-commands that don’t need a repository, configuration should + /// still be affected by a repository’s `.hg/hgrc` file. This is the + /// constructor to use. + pub fn find_optional( + config: &Config, + explicit_path: Option<&Path>, + ) -> Result, RepoError> { + match Self::find(config, explicit_path) { + Ok(repo) => Ok(Some(repo)), + Err(RepoError::NotFound { .. }) if explicit_path.is_none() => { + Ok(None) + } + Err(error) => Err(error), + } + } + /// To be called after checking that `.hg` is a sub-directory fn new_at_path( working_directory: PathBuf, diff -r 2e5dd18d6dc3 -r a25033eb43b5 rust/hg-core/src/utils.rs --- a/rust/hg-core/src/utils.rs Mon Feb 08 23:08:44 2021 +0100 +++ b/rust/hg-core/src/utils.rs Mon Feb 08 23:41:58 2021 +0100 @@ -67,6 +67,7 @@ fn trim_start(&self) -> &Self; fn trim(&self) -> &Self; fn drop_prefix(&self, needle: &Self) -> Option<&Self>; + fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>; } #[allow(clippy::trivially_copy_pass_by_ref)] @@ -116,6 +117,13 @@ None } } + + fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])> { + let mut iter = self.splitn(2, |&byte| byte == separator); + let a = iter.next()?; + let b = iter.next()?; + Some((a, b)) + } } pub trait Escaped { diff -r 2e5dd18d6dc3 -r a25033eb43b5 rust/rhg/src/commands/config.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/rhg/src/commands/config.rs Mon Feb 08 23:41:58 2021 +0100 @@ -0,0 +1,52 @@ +use crate::error::CommandError; +use crate::ui::Ui; +use clap::Arg; +use clap::ArgMatches; +use format_bytes::format_bytes; +use hg::config::Config; +use hg::errors::HgError; +use hg::repo::Repo; +use hg::utils::SliceExt; +use std::path::Path; + +pub const HELP_TEXT: &str = " +With one argument of the form section.name, print just the value of that config item. +"; + +pub fn args() -> clap::App<'static, 'static> { + clap::SubCommand::with_name("config") + .arg( + Arg::with_name("name") + .help("the section.name to print") + .value_name("NAME") + .required(true) + .takes_value(true), + ) + .about(HELP_TEXT) +} + +pub fn run( + ui: &Ui, + config: &Config, + repo_path: Option<&Path>, + args: &ArgMatches, +) -> Result<(), CommandError> { + let opt_repo = Repo::find_optional(config, repo_path)?; + let config = if let Some(repo) = &opt_repo { + repo.config() + } else { + config + }; + + let (section, name) = args + .value_of("name") + .expect("missing required CLI argument") + .as_bytes() + .split_2(b'.') + .ok_or_else(|| HgError::abort(""))?; + + let value = config.get(section, name).unwrap_or(b""); + + ui.write_stdout(&format_bytes!(b"{}\n", value))?; + Ok(()) +} diff -r 2e5dd18d6dc3 -r a25033eb43b5 rust/rhg/src/main.rs --- a/rust/rhg/src/main.rs Mon Feb 08 23:08:44 2021 +0100 +++ b/rust/rhg/src/main.rs Mon Feb 08 23:41:58 2021 +0100 @@ -134,4 +134,5 @@ debugrequirements files root + config } diff -r 2e5dd18d6dc3 -r a25033eb43b5 tests/test-rhg.t --- a/tests/test-rhg.t Mon Feb 08 23:08:44 2021 +0100 +++ b/tests/test-rhg.t Mon Feb 08 23:41:58 2021 +0100 @@ -30,6 +30,18 @@ $ rhg root $TESTTMP/repository +Reading and setting configuration + $ echo "[ui]" >> $HGRCPATH + $ echo "username = user1" >> $HGRCPATH + $ rhg config ui.username + user1 + $ echo "[ui]" >> .hg/hgrc + $ echo "username = user2" >> .hg/hgrc + $ rhg config ui.username + user2 + $ rhg --config ui.username=user3 config ui.username + user3 + Unwritable file descriptor $ rhg root > /dev/full abort: No space left on device (os error 28)