##// END OF EJS Templates
rhg: Make configuration available as early as possible in main()...
Simon Sapin -
r47423:7284b524 default
parent child Browse files
Show More
@@ -52,20 +52,22 b" impl<'a> Blackbox<'a> {"
52 52 process_start_time: &'a ProcessStartTime,
53 53 ) -> Result<Self, HgError> {
54 54 let configured = if let Ok(repo) = invocation.repo {
55 let config = invocation.config();
56 if config.get(b"extensions", b"blackbox").is_none() {
55 if invocation.config.get(b"extensions", b"blackbox").is_none() {
57 56 // The extension is not enabled
58 57 None
59 58 } else {
60 59 Some(ConfiguredBlackbox {
61 60 repo,
62 max_size: config
61 max_size: invocation
62 .config
63 63 .get_byte_size(b"blackbox", b"maxsize")?
64 64 .unwrap_or(DEFAULT_MAX_SIZE),
65 max_files: config
65 max_files: invocation
66 .config
66 67 .get_u32(b"blackbox", b"maxfiles")?
67 68 .unwrap_or(DEFAULT_MAX_FILES),
68 date_format: config
69 date_format: invocation
70 .config
69 71 .get_str(b"blackbox", b"date-format")?
70 72 .unwrap_or(DEFAULT_DATE_FORMAT),
71 73 })
@@ -29,7 +29,7 b' pub fn run(invocation: &crate::CliInvoca'
29 29 .split_2(b'.')
30 30 .ok_or_else(|| HgError::abort(""))?;
31 31
32 let value = invocation.config().get(section, name).unwrap_or(b"");
32 let value = invocation.config.get(section, name).unwrap_or(b"");
33 33
34 34 invocation.ui.write_stdout(&format_bytes!(b"{}\n", value))?;
35 35 Ok(())
@@ -7,7 +7,10 b' use clap::ArgMatches;'
7 7 use format_bytes::format_bytes;
8 8 use hg::config::Config;
9 9 use hg::repo::{Repo, RepoError};
10 use std::path::{Path, PathBuf};
10 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
11 use hg::utils::SliceExt;
12 use std::ffi::OsString;
13 use std::path::PathBuf;
11 14
12 15 mod blackbox;
13 16 mod error;
@@ -16,10 +19,11 b' mod ui;'
16 19 use error::CommandError;
17 20
18 21 fn main_with_result(
22 process_start_time: &blackbox::ProcessStartTime,
19 23 ui: &ui::Ui,
20 process_start_time: &blackbox::ProcessStartTime,
24 repo: Result<&Repo, &NoRepoInCwdError>,
25 config: &Config,
21 26 ) -> Result<(), CommandError> {
22 env_logger::init();
23 27 let app = App::new("rhg")
24 28 .global_setting(AppSettings::AllowInvalidUtf8)
25 29 .setting(AppSettings::SubcommandRequired)
@@ -57,29 +61,11 b' fn main_with_result('
57 61 let subcommand_args = subcommand_matches
58 62 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
59 63
60 let config_args = matches
61 .values_of_os("config")
62 // Turn `Option::None` into an empty iterator:
63 .into_iter()
64 .flatten()
65 .map(hg::utils::files::get_bytes_from_os_str);
66 let non_repo_config = &hg::config::Config::load(config_args)?;
67
68 let repo_path = matches.value_of_os("repository").map(Path::new);
69 let repo = match Repo::find(non_repo_config, repo_path) {
70 Ok(repo) => Ok(repo),
71 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
72 // Not finding a repo is not fatal yet, if `-R` was not given
73 Err(NoRepoInCwdError { cwd: at })
74 }
75 Err(error) => return Err(error.into()),
76 };
77
78 64 let invocation = CliInvocation {
79 65 ui,
80 66 subcommand_args,
81 non_repo_config,
82 repo: repo.as_ref(),
67 config,
68 repo,
83 69 };
84 70 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
85 71 blackbox.log_command_start();
@@ -94,17 +80,36 b' fn main() {'
94 80 // measurements. Reading config files can be slow if they’re on NFS.
95 81 let process_start_time = blackbox::ProcessStartTime::now();
96 82
83 env_logger::init();
97 84 let ui = ui::Ui::new();
98 85
99 let result = main_with_result(&ui, &process_start_time);
100 if let Err(CommandError::Abort { message }) = &result {
101 if !message.is_empty() {
102 // Ignore errors when writing to stderr, we’re already exiting
103 // with failure code so there’s not much more we can do.
104 let _ = ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
86 let early_args = EarlyArgs::parse(std::env::args_os());
87 let non_repo_config = Config::load(early_args.config)
88 .unwrap_or_else(|error| exit(&ui, Err(error.into())));
89
90 let repo_path = early_args.repo.as_deref().map(get_path_from_bytes);
91 let repo_result = match Repo::find(&non_repo_config, repo_path) {
92 Ok(repo) => Ok(repo),
93 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
94 // Not finding a repo is not fatal yet, if `-R` was not given
95 Err(NoRepoInCwdError { cwd: at })
105 96 }
106 }
107 std::process::exit(exit_code(&result))
97 Err(error) => exit(&ui, Err(error.into())),
98 };
99
100 let config = if let Ok(repo) = &repo_result {
101 repo.config()
102 } else {
103 &non_repo_config
104 };
105
106 let result = main_with_result(
107 &process_start_time,
108 &ui,
109 repo_result.as_ref(),
110 config,
111 );
112 exit(&ui, result)
108 113 }
109 114
110 115 fn exit_code(result: &Result<(), CommandError>) -> i32 {
@@ -118,6 +123,17 b' fn exit_code(result: &Result<(), Command'
118 123 }
119 124 }
120 125
126 fn exit(ui: &Ui, result: Result<(), CommandError>) -> ! {
127 if let Err(CommandError::Abort { message }) = &result {
128 if !message.is_empty() {
129 // Ignore errors when writing to stderr, we’re already exiting
130 // with failure code so there’s not much more we can do.
131 let _ = ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
132 }
133 }
134 std::process::exit(exit_code(&result))
135 }
136
121 137 macro_rules! subcommands {
122 138 ($( $command: ident )+) => {
123 139 mod commands {
@@ -157,7 +173,7 b' subcommands! {'
157 173 pub struct CliInvocation<'a> {
158 174 ui: &'a Ui,
159 175 subcommand_args: &'a ArgMatches<'a>,
160 non_repo_config: &'a Config,
176 config: &'a Config,
161 177 /// References inside `Result` is a bit peculiar but allow
162 178 /// `invocation.repo?` to work out with `&CliInvocation` since this
163 179 /// `Result` type is `Copy`.
@@ -168,12 +184,45 b' struct NoRepoInCwdError {'
168 184 cwd: PathBuf,
169 185 }
170 186
171 impl CliInvocation<'_> {
172 fn config(&self) -> &Config {
173 if let Ok(repo) = self.repo {
174 repo.config()
175 } else {
176 self.non_repo_config
187 /// CLI arguments to be parsed "early" in order to be able to read
188 /// configuration before using Clap. Ideally we would also use Clap for this,
189 /// see <https://github.com/clap-rs/clap/discussions/2366>.
190 ///
191 /// These arguments are still declared when we do use Clap later, so that Clap
192 /// does not return an error for their presence.
193 struct EarlyArgs {
194 /// Values of all `--config` arguments. (Possibly none)
195 config: Vec<Vec<u8>>,
196 /// Value of the `-R` or `--repository` argument, if any.
197 repo: Option<Vec<u8>>,
198 }
199
200 impl EarlyArgs {
201 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
202 let mut args = args.into_iter().map(get_bytes_from_os_str);
203 let mut config = Vec::new();
204 let mut repo = None;
205 // Use `while let` instead of `for` so that we can also call
206 // `args.next()` inside the loop.
207 while let Some(arg) = args.next() {
208 if arg == b"--config" {
209 if let Some(value) = args.next() {
210 config.push(value)
211 }
212 } else if let Some(value) = arg.drop_prefix(b"--config=") {
213 config.push(value.to_owned())
214 }
215
216 if arg == b"--repository" || arg == b"-R" {
217 if let Some(value) = args.next() {
218 repo = Some(value)
219 }
220 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
221 repo = Some(value.to_owned())
222 } else if let Some(value) = arg.drop_prefix(b"-R") {
223 repo = Some(value.to_owned())
224 }
177 225 }
226 Self { config, repo }
178 227 }
179 228 }
General Comments 0
You need to be logged in to leave comments. Login now