comparison rust/rhg/src/main.rs @ 46705:33f2d56acc73

rhg: Add a `rhg.on-unsupported` configuration key For now the two values are: * `abort-silent`: silently exit with code 252, the previous default behavior * `abort`: print an error message about what feature is not supported, then exit with code 252. Now the default. Differential Revision: https://phab.mercurial-scm.org/D10091
author Simon Sapin <simon.sapin@octobus.net>
date Mon, 01 Mar 2021 16:18:42 +0100
parents 7284b524b441
children 93e9f448273c
comparison
equal deleted inserted replaced
46704:7284b524b441 46705:33f2d56acc73
82 82
83 env_logger::init(); 83 env_logger::init();
84 let ui = ui::Ui::new(); 84 let ui = ui::Ui::new();
85 85
86 let early_args = EarlyArgs::parse(std::env::args_os()); 86 let early_args = EarlyArgs::parse(std::env::args_os());
87 let non_repo_config = Config::load(early_args.config) 87 let non_repo_config =
88 .unwrap_or_else(|error| exit(&ui, Err(error.into()))); 88 Config::load(early_args.config).unwrap_or_else(|error| {
89 // Normally this is decided based on config, but we don’t have that
90 // available. As of this writing config loading never returns an
91 // "unsupported" error but that is not enforced by the type system.
92 let on_unsupported = OnUnsupported::Abort;
93
94 exit(&ui, on_unsupported, Err(error.into()))
95 });
89 96
90 let repo_path = early_args.repo.as_deref().map(get_path_from_bytes); 97 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) { 98 let repo_result = match Repo::find(&non_repo_config, repo_path) {
92 Ok(repo) => Ok(repo), 99 Ok(repo) => Ok(repo),
93 Err(RepoError::NotFound { at }) if repo_path.is_none() => { 100 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
94 // Not finding a repo is not fatal yet, if `-R` was not given 101 // Not finding a repo is not fatal yet, if `-R` was not given
95 Err(NoRepoInCwdError { cwd: at }) 102 Err(NoRepoInCwdError { cwd: at })
96 } 103 }
97 Err(error) => exit(&ui, Err(error.into())), 104 Err(error) => exit(
105 &ui,
106 OnUnsupported::from_config(&non_repo_config),
107 Err(error.into()),
108 ),
98 }; 109 };
99 110
100 let config = if let Ok(repo) = &repo_result { 111 let config = if let Ok(repo) = &repo_result {
101 repo.config() 112 repo.config()
102 } else { 113 } else {
107 &process_start_time, 118 &process_start_time,
108 &ui, 119 &ui,
109 repo_result.as_ref(), 120 repo_result.as_ref(),
110 config, 121 config,
111 ); 122 );
112 exit(&ui, result) 123 exit(&ui, OnUnsupported::from_config(config), result)
113 } 124 }
114 125
115 fn exit_code(result: &Result<(), CommandError>) -> i32 { 126 fn exit_code(result: &Result<(), CommandError>) -> i32 {
116 match result { 127 match result {
117 Ok(()) => exitcode::OK, 128 Ok(()) => exitcode::OK,
118 Err(CommandError::Abort { .. }) => exitcode::ABORT, 129 Err(CommandError::Abort { .. }) => exitcode::ABORT,
119 130
120 // Exit with a specific code and no error message to let a potential 131 // Exit with a specific code and no error message to let a potential
121 // wrapper script fallback to Python-based Mercurial. 132 // wrapper script fallback to Python-based Mercurial.
122 Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, 133 Err(CommandError::UnsupportedFeature { .. }) => {
123 } 134 exitcode::UNIMPLEMENTED
124 } 135 }
125 136 }
126 fn exit(ui: &Ui, result: Result<(), CommandError>) -> ! { 137 }
127 if let Err(CommandError::Abort { message }) = &result { 138
128 if !message.is_empty() { 139 fn exit(
129 // Ignore errors when writing to stderr, we’re already exiting 140 ui: &Ui,
130 // with failure code so there’s not much more we can do. 141 on_unsupported: OnUnsupported,
131 let _ = ui.write_stderr(&format_bytes!(b"abort: {}\n", message)); 142 result: Result<(), CommandError>,
143 ) -> ! {
144 match &result {
145 Ok(_) => {}
146 Err(CommandError::Abort { message }) => {
147 if !message.is_empty() {
148 // Ignore errors when writing to stderr, we’re already exiting
149 // with failure code so there’s not much more we can do.
150 let _ =
151 ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
152 }
153 }
154 Err(CommandError::UnsupportedFeature { message }) => {
155 match on_unsupported {
156 OnUnsupported::Abort => {
157 let _ = ui.write_stderr(&format_bytes!(
158 b"unsupported feature: {}\n",
159 message
160 ));
161 }
162 OnUnsupported::AbortSilent => {}
163 }
132 } 164 }
133 } 165 }
134 std::process::exit(exit_code(&result)) 166 std::process::exit(exit_code(&result))
135 } 167 }
136 168
224 } 256 }
225 } 257 }
226 Self { config, repo } 258 Self { config, repo }
227 } 259 }
228 } 260 }
261
262 /// What to do when encountering some unsupported feature.
263 ///
264 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
265 enum OnUnsupported {
266 /// Print an error message describing what feature is not supported,
267 /// and exit with code 252.
268 Abort,
269 /// Silently exit with code 252.
270 AbortSilent,
271 }
272
273 impl OnUnsupported {
274 fn from_config(config: &Config) -> Self {
275 let default = OnUnsupported::Abort;
276 match config.get(b"rhg", b"on-unsupported") {
277 Some(b"abort") => OnUnsupported::Abort,
278 Some(b"abort-silent") => OnUnsupported::AbortSilent,
279 None => default,
280 Some(_) => {
281 // TODO: warn about unknown config value
282 default
283 }
284 }
285 }
286 }