Skip to content

Commit

Permalink
Add a "config" command with "get" and "list" subcommands
Browse files Browse the repository at this point in the history
Partially fixes jj-vcs#531.
  • Loading branch information
dbarnett committed Dec 20, 2022
1 parent a2d2da4 commit 2fe202d
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
20 changes: 20 additions & 0 deletions src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1221,6 +1221,26 @@ pub fn write_commit_summary(
Ok(())
}

pub fn write_config_entry(
ui: &mut Ui,
path: &str,
value: config::Value,
) -> Result<(), CommandError> {
match value.kind {
config::ValueKind::Table(table) => {
for (key, table_val) in table {
let key_path = match path {
"" => key,
_ => format!("{path}.{key}"),
};
write_config_entry(ui, key_path.as_str(), table_val)?;
}
}
_ => writeln!(ui, "{path}={value}")?,
};
Ok(())
}

pub fn short_commit_description(commit: &Commit) -> String {
let first_line = commit.description().split('\n').next().unwrap();
format!("{} ({})", short_commit_hash(commit.id()), first_line)
Expand Down
63 changes: 62 additions & 1 deletion src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use std::{fs, io};
use chrono::{FixedOffset, LocalResult, TimeZone, Utc};
use clap::builder::NonEmptyStringValueParser;
use clap::{ArgGroup, ArgMatches, CommandFactory, FromArgMatches, Subcommand};
use config::Source;
use itertools::Itertools;
use jujutsu_lib::backend::{CommitId, Timestamp, TreeValue};
use jujutsu_lib::commit::Commit;
Expand Down Expand Up @@ -54,7 +55,8 @@ use pest::Parser;
use crate::cli_util::{
check_stale_working_copy, print_checkout_stats, print_failed_git_export, resolve_base_revs,
short_commit_description, short_commit_hash, user_error, user_error_with_hint,
write_commit_summary, Args, CommandError, CommandHelper, RevisionArg, WorkspaceCommandHelper,
write_commit_summary, write_config_entry, Args, CommandError, CommandHelper, RevisionArg,
WorkspaceCommandHelper,
};
use crate::config::FullCommandArgs;
use crate::diff_util::{self, DiffFormat, DiffFormatArgs};
Expand All @@ -69,6 +71,8 @@ use crate::ui::Ui;
enum Commands {
Version(VersionArgs),
Init(InitArgs),
#[command(subcommand)]
Config(ConfigSubcommand),
Checkout(CheckoutArgs),
Untrack(UntrackArgs),
Files(FilesArgs),
Expand Down Expand Up @@ -142,6 +146,33 @@ struct InitArgs {
git_repo: Option<String>,
}

/// Get config options
///
/// Operates on jj configuration, which comes from the config file and
/// environment variables. Uses the config file at ~/.jjconfig.toml or
/// $XDG_CONFIG_HOME/jj/config.toml, unless overridden with the JJ_CONFIG
/// environment variable.
///
/// For supported config options and more details about jj config, see
/// https://github.com/martinvonz/jj/blob/main/docs/config.md.
///
/// Note: Currently only supports getting config options, but support for
/// setting options is also planned (see
/// https://github.com/martinvonz/jj/issues/531).
#[derive(clap::Subcommand, Clone, Debug)]
enum ConfigSubcommand {
/// Get the value for a given key.
Get {
/// The config option name
#[arg(required = true, value_parser=NonEmptyStringValueParser::new())]
name: String,
},

/// List all variables set in config file, along with their values.
#[command(visible_alias("l"))]
List {},
}

/// Create a new, empty change and edit it in the working copy
///
/// For more information, see
Expand Down Expand Up @@ -1156,6 +1187,35 @@ Set `ui.allow-init-native` to allow initializing a repo with the native backend.
Ok(())
}

fn cmd_config(
ui: &mut Ui,
_command: &CommandHelper,
subcommand: &ConfigSubcommand,
) -> Result<(), CommandError> {
ui.request_pager();
match subcommand {
ConfigSubcommand::Get { name } => {
let raw_value = ui.settings().config().get::<config::Value>(name);
let value = raw_value.map_err(|e| match e {
config::ConfigError::NotFound { .. } => user_error("key not found in config"),
_ => e.into(),
})?;
match value.kind {
config::ValueKind::Table(table) => {
write_config_entry(ui, &name.to_string(), table.into())?
}
_ => writeln!(ui, "{value}")?,
};
}
ConfigSubcommand::List {} => {
let config_table = ui.settings().config().collect()?;
write_config_entry(ui, "", config_table.into())?;
}
}

Ok(())
}

fn cmd_checkout(
ui: &mut Ui,
command: &CommandHelper,
Expand Down Expand Up @@ -4214,6 +4274,7 @@ pub fn run_command(
match &derived_subcommands {
Commands::Version(sub_args) => cmd_version(ui, command_helper, sub_args),
Commands::Init(sub_args) => cmd_init(ui, command_helper, sub_args),
Commands::Config(sub_args) => cmd_config(ui, command_helper, sub_args),
Commands::Checkout(sub_args) => cmd_checkout(ui, command_helper, sub_args),
Commands::Untrack(sub_args) => cmd_untrack(ui, command_helper, sub_args),
Commands::Files(sub_args) => cmd_files(ui, command_helper, sub_args),
Expand Down
56 changes: 56 additions & 0 deletions tests/test_config_command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2022 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use regex::Regex;

use crate::common::TestEnvironment;

pub mod common;

#[test]
fn test_config_get() {
// TODO: Populate expected config values explicitly in this file.
let test_env = TestEnvironment::default();
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "user.name"]);
insta::assert_snapshot!(stdout, @"Test User");
}

#[test]
fn test_config_get_table() {
let test_env = TestEnvironment::default();
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "user"]);
insta::assert_snapshot!(
find_stdout_line("user.name", &stdout),
@"user.name=Test User");
insta::assert_snapshot!(
find_stdout_line("user.email", &stdout),
@"[email protected]");
}

#[test]
fn test_config_list() {
let test_env = TestEnvironment::default();
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list"]);
insta::assert_snapshot!(
find_stdout_line("user.name", &stdout),
@"user.name=Test User");
insta::assert_snapshot!(
find_stdout_line("operation.hostname", &stdout),
@"operation.hostname=host.example.com");
}

fn find_stdout_line<'a>(keyname: &str, stdout: &'a str) -> &'a str {
let key_line_re = Regex::new(&format!(r"(?m)^{keyname}=.*$")).unwrap();
key_line_re.find(stdout).unwrap().as_str()
}

0 comments on commit 2fe202d

Please sign in to comment.