just playing with tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cli: move a few functions in `commands/config.rs` to public places

Turns out we use some of the functions in `commands/config.rs` at
Google. (We use them for writing name and email if the user hasn't set
them.)

authored by

Martin von Zweigbergk and committed by
Martin von Zweigbergk
ff4ea73a b227dde7

+93 -86
+22 -1
cli/src/cli_util.rs
··· 83 83 CommandError, 84 84 }; 85 85 use crate::commit_templater::{CommitTemplateLanguage, CommitTemplateLanguageExtension}; 86 - use crate::config::{AnnotatedValue, CommandNameAndArgs, LayeredConfigs}; 86 + use crate::config::{ 87 + new_config_path, AnnotatedValue, CommandNameAndArgs, ConfigSource, LayeredConfigs, 88 + }; 87 89 use crate::diff_util::{self, DiffFormat, DiffFormatArgs, DiffRenderer, DiffWorkspaceContext}; 88 90 use crate::formatter::{FormatRecorder, Formatter, PlainTextFormatter}; 89 91 use crate::git_util::{ ··· 2125 2127 } 2126 2128 } 2127 2129 } 2130 + } 2131 + 2132 + pub fn get_new_config_file_path( 2133 + config_source: &ConfigSource, 2134 + command: &CommandHelper, 2135 + ) -> Result<PathBuf, CommandError> { 2136 + let edit_path = match config_source { 2137 + // TODO(#531): Special-case for editors that can't handle viewing directories? 2138 + ConfigSource::User => { 2139 + new_config_path()?.ok_or_else(|| user_error("No repo config path found to edit"))? 2140 + } 2141 + ConfigSource::Repo => command.workspace_loader()?.repo_path().join("config.toml"), 2142 + _ => { 2143 + return Err(user_error(format!( 2144 + "Can't get path for config source {config_source:?}" 2145 + ))); 2146 + } 2147 + }; 2148 + Ok(edit_path) 2128 2149 } 2129 2150 2130 2151 pub fn run_ui_editor(settings: &UserSettings, edit_path: &PathBuf) -> Result<(), CommandError> {
+3 -85
cli/src/commands/config.rs
··· 14 14 15 15 use std::fmt; 16 16 use std::io::Write; 17 - use std::path::{Path, PathBuf}; 18 17 19 18 use clap::builder::NonEmptyStringValueParser; 20 19 use itertools::Itertools; 21 20 use tracing::instrument; 22 21 23 - use crate::cli_util::{run_ui_editor, CommandHelper}; 24 - use crate::command_error::{config_error, user_error, user_error_with_message, CommandError}; 25 - use crate::config::{new_config_path, AnnotatedValue, ConfigSource}; 22 + use crate::cli_util::{get_new_config_file_path, run_ui_editor, CommandHelper}; 23 + use crate::command_error::{config_error, user_error, CommandError}; 24 + use crate::config::{write_config_value_to_file, AnnotatedValue, ConfigSource}; 26 25 use crate::generic_templater::GenericTemplateLanguage; 27 26 use crate::template_builder::TemplateLanguage as _; 28 27 use crate::templater::TemplatePropertyExt as _; ··· 197 196 .collect(), 198 197 config::ValueKind::Array(ref array) => array.iter().map(to_toml_value).collect(), 199 198 } 200 - } 201 - 202 - fn write_config_value_to_file(key: &str, value_str: &str, path: &Path) -> Result<(), CommandError> { 203 - // Read config 204 - let config_toml = std::fs::read_to_string(path).or_else(|err| { 205 - match err.kind() { 206 - // If config doesn't exist yet, read as empty and we'll write one. 207 - std::io::ErrorKind::NotFound => Ok("".to_string()), 208 - _ => Err(user_error_with_message( 209 - format!("Failed to read file {path}", path = path.display()), 210 - err, 211 - )), 212 - } 213 - })?; 214 - let mut doc: toml_edit::Document = config_toml.parse().map_err(|err| { 215 - user_error_with_message( 216 - format!("Failed to parse file {path}", path = path.display()), 217 - err, 218 - ) 219 - })?; 220 - 221 - // Apply config value 222 - // Interpret value as string if it can't be parsed as a TOML value. 223 - // TODO(#531): Infer types based on schema (w/ --type arg to override). 224 - let item = match value_str.parse() { 225 - Ok(value) => toml_edit::Item::Value(value), 226 - _ => toml_edit::value(value_str), 227 - }; 228 - let mut target_table = doc.as_table_mut(); 229 - let mut key_parts_iter = key.split('.'); 230 - // Note: split guarantees at least one item. 231 - let last_key_part = key_parts_iter.next_back().unwrap(); 232 - for key_part in key_parts_iter { 233 - target_table = target_table 234 - .entry(key_part) 235 - .or_insert_with(|| toml_edit::Item::Table(toml_edit::Table::new())) 236 - .as_table_mut() 237 - .ok_or_else(|| { 238 - user_error(format!( 239 - "Failed to set {key}: would overwrite non-table value with parent table" 240 - )) 241 - })?; 242 - } 243 - // Error out if overwriting non-scalar value for key (table or array) with 244 - // scalar. 245 - match target_table.get(last_key_part) { 246 - None | Some(toml_edit::Item::None | toml_edit::Item::Value(_)) => {} 247 - Some(toml_edit::Item::Table(_) | toml_edit::Item::ArrayOfTables(_)) => { 248 - return Err(user_error(format!( 249 - "Failed to set {key}: would overwrite entire table" 250 - ))); 251 - } 252 - } 253 - target_table[last_key_part] = item; 254 - 255 - // Write config back 256 - std::fs::write(path, doc.to_string()).map_err(|err| { 257 - user_error_with_message( 258 - format!("Failed to write file {path}", path = path.display()), 259 - err, 260 - ) 261 - }) 262 - } 263 - 264 - fn get_new_config_file_path( 265 - config_source: &ConfigSource, 266 - command: &CommandHelper, 267 - ) -> Result<PathBuf, CommandError> { 268 - let edit_path = match config_source { 269 - // TODO(#531): Special-case for editors that can't handle viewing directories? 270 - ConfigSource::User => { 271 - new_config_path()?.ok_or_else(|| user_error("No repo config path found to edit"))? 272 - } 273 - ConfigSource::Repo => command.workspace_loader()?.repo_path().join("config.toml"), 274 - _ => { 275 - return Err(user_error(format!( 276 - "Can't get path for config source {config_source:?}" 277 - ))); 278 - } 279 - }; 280 - Ok(edit_path) 281 199 } 282 200 283 201 // AnnotatedValue will be cloned internally in the templater. If the cloning
+68
cli/src/config.rs
··· 24 24 use thiserror::Error; 25 25 use tracing::instrument; 26 26 27 + use crate::command_error::{user_error, user_error_with_message, CommandError}; 28 + 27 29 #[derive(Error, Debug)] 28 30 pub enum ConfigError { 29 31 #[error(transparent)] ··· 432 434 ) 433 435 }) 434 436 .build() 437 + } 438 + 439 + pub fn write_config_value_to_file( 440 + key: &str, 441 + value_str: &str, 442 + path: &Path, 443 + ) -> Result<(), CommandError> { 444 + // Read config 445 + let config_toml = std::fs::read_to_string(path).or_else(|err| { 446 + match err.kind() { 447 + // If config doesn't exist yet, read as empty and we'll write one. 448 + std::io::ErrorKind::NotFound => Ok("".to_string()), 449 + _ => Err(user_error_with_message( 450 + format!("Failed to read file {path}", path = path.display()), 451 + err, 452 + )), 453 + } 454 + })?; 455 + let mut doc: toml_edit::Document = config_toml.parse().map_err(|err| { 456 + user_error_with_message( 457 + format!("Failed to parse file {path}", path = path.display()), 458 + err, 459 + ) 460 + })?; 461 + 462 + // Apply config value 463 + // Interpret value as string if it can't be parsed as a TOML value. 464 + // TODO(#531): Infer types based on schema (w/ --type arg to override). 465 + let item = match value_str.parse() { 466 + Ok(value) => toml_edit::Item::Value(value), 467 + _ => toml_edit::value(value_str), 468 + }; 469 + let mut target_table = doc.as_table_mut(); 470 + let mut key_parts_iter = key.split('.'); 471 + // Note: split guarantees at least one item. 472 + let last_key_part = key_parts_iter.next_back().unwrap(); 473 + for key_part in key_parts_iter { 474 + target_table = target_table 475 + .entry(key_part) 476 + .or_insert_with(|| toml_edit::Item::Table(toml_edit::Table::new())) 477 + .as_table_mut() 478 + .ok_or_else(|| { 479 + user_error(format!( 480 + "Failed to set {key}: would overwrite non-table value with parent table" 481 + )) 482 + })?; 483 + } 484 + // Error out if overwriting non-scalar value for key (table or array) with 485 + // scalar. 486 + match target_table.get(last_key_part) { 487 + None | Some(toml_edit::Item::None | toml_edit::Item::Value(_)) => {} 488 + Some(toml_edit::Item::Table(_) | toml_edit::Item::ArrayOfTables(_)) => { 489 + return Err(user_error(format!( 490 + "Failed to set {key}: would overwrite entire table" 491 + ))); 492 + } 493 + } 494 + target_table[last_key_part] = item; 495 + 496 + // Write config back 497 + std::fs::write(path, doc.to_string()).map_err(|err| { 498 + user_error_with_message( 499 + format!("Failed to write file {path}", path = path.display()), 500 + err, 501 + ) 502 + }) 435 503 } 436 504 437 505 /// Command name and arguments specified by config.