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: con |
|
|
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: con |
|
|
65 | max_files: invocation | |
|
66 | .config | |
|
66 | 67 | .get_u32(b"blackbox", b"maxfiles")? |
|
67 | 68 | .unwrap_or(DEFAULT_MAX_FILES), |
|
68 |
date_format: con |
|
|
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 |
|
|
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 |
|
|
|
82 |
repo |
|
|
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 |
|
|
|
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