config: forward write_config_value_to_file() to ConfigLayer::set_value()

write_config_value_to_file() will be inlined to callers.
This commit is contained in:
Yuya Nishihara 2024-12-12 15:53:31 +09:00
parent cf6711437f
commit 4d67d3eeca
2 changed files with 28 additions and 52 deletions

View file

@ -426,23 +426,20 @@ pub fn parse_config_args(toml_strs: &[ConfigArg]) -> Result<Vec<ConfigLayer>, Co
.try_collect() .try_collect()
} }
fn read_config(path: &Path) -> Result<toml_edit::ImDocument<String>, CommandError> { fn load_config_file_or_empty(
let config_toml = std::fs::read_to_string(path).or_else(|err| { source: ConfigSource,
match err.kind() { path: &Path,
) -> Result<ConfigLayer, ConfigLoadError> {
match ConfigLayer::load_from_file(source, path.into()) {
Ok(layer) => Ok(layer),
Err(ConfigLoadError::Read(err)) if err.error.kind() == std::io::ErrorKind::NotFound => {
// If config doesn't exist yet, read as empty and we'll write one. // If config doesn't exist yet, read as empty and we'll write one.
std::io::ErrorKind::NotFound => Ok("".to_string()), let mut layer = ConfigLayer::empty(source);
_ => Err(user_error_with_message( layer.path = Some(path.into());
format!("Failed to read file {path}", path = path.display()), Ok(layer)
err, }
)), Err(err) => Err(err),
} }
})?;
config_toml.parse().map_err(|err| {
user_error_with_message(
format!("Failed to parse file {path}", path = path.display()),
err,
)
})
} }
fn write_config(path: &Path, doc: &toml_edit::DocumentMut) -> Result<(), CommandError> { fn write_config(path: &Path, doc: &toml_edit::DocumentMut) -> Result<(), CommandError> {
@ -459,43 +456,20 @@ pub fn write_config_value_to_file(
value: toml_edit::Value, value: toml_edit::Value,
path: &Path, path: &Path,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
let mut doc = read_config(path)?.into_mut(); // TODO: Load config layer by caller. Here we use a dummy source for now.
let mut layer = load_config_file_or_empty(ConfigSource::User, path)?;
// Apply config value layer
let mut target_table = doc.as_table_mut(); .set_value(key, value)
let mut key_parts_iter = key.components(); .map_err(|err| user_error_with_message(format!("Failed to set {key}"), err))?;
let last_key_part = key_parts_iter.next_back().expect("key must not be empty"); write_config(path, &layer.data)
for key_part in key_parts_iter {
target_table = target_table
.entry(key_part)
.or_insert_with(|| toml_edit::Item::Table(toml_edit::Table::new()))
.as_table_mut()
.ok_or_else(|| {
user_error(format!(
"Failed to set {key}: would overwrite non-table value with parent table"
))
})?;
}
// Error out if overwriting non-scalar value for key (table or array) with
// scalar.
match target_table.get(last_key_part) {
None | Some(toml_edit::Item::None | toml_edit::Item::Value(_)) => {}
Some(toml_edit::Item::Table(_) | toml_edit::Item::ArrayOfTables(_)) => {
return Err(user_error(format!(
"Failed to set {key}: would overwrite entire table"
)));
}
}
target_table[last_key_part] = toml_edit::Item::Value(value);
write_config(path, &doc)
} }
pub fn remove_config_value_from_file( pub fn remove_config_value_from_file(
key: &ConfigNamePathBuf, key: &ConfigNamePathBuf,
path: &Path, path: &Path,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
let mut doc = read_config(path)?.into_mut(); // TODO: Load config layer by caller. Here we use a dummy source for now.
let mut doc = load_config_file_or_empty(ConfigSource::User, path)?.data;
// Find target table // Find target table
let mut key_iter = key.components(); let mut key_iter = key.components();

View file

@ -578,9 +578,10 @@ fn test_config_set_type_mismatch() {
&repo_path, &repo_path,
&["config", "set", "--user", "test-table", "not-a-table"], &["config", "set", "--user", "test-table", "not-a-table"],
); );
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r"
Error: Failed to set test-table: would overwrite entire table Error: Failed to set test-table
"###); Caused by: Would overwrite entire table test-table
");
// But it's fine to overwrite arrays and inline tables // But it's fine to overwrite arrays and inline tables
test_env.jj_cmd_success( test_env.jj_cmd_success(
@ -617,9 +618,10 @@ fn test_config_set_nontable_parent() {
&repo_path, &repo_path,
&["config", "set", "--user", "test-nontable.foo", "test-val"], &["config", "set", "--user", "test-nontable.foo", "test-val"],
); );
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r"
Error: Failed to set test-nontable.foo: would overwrite non-table value with parent table Error: Failed to set test-nontable.foo
"###); Caused by: Would overwrite non-table value with parent table test-nontable
");
} }
#[test] #[test]