Skip to content

Commit 8941384

Browse files
committed
Do not panic when reading from env var
1 parent 3121ef4 commit 8941384

File tree

1 file changed

+51
-17
lines changed

1 file changed

+51
-17
lines changed

src/util/options.rs

+51-17
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,16 @@ fn always_valid<T>(_: &T) -> bool {
124124
true
125125
}
126126

127+
/// Error when setting an option by option name and option value as strings.
128+
enum SetOptionByStringError {
129+
/// The option name does not exist.
130+
InvalidKey,
131+
/// Error when converting the value from string.
132+
ValueParseError,
133+
/// The value failed validation.
134+
ValueValidationError,
135+
}
136+
127137
/// An MMTk option of a given type.
128138
/// This type allows us to store some metadata for the option. To get the value of an option,
129139
/// you can simply dereference it (for example, *options.threads).
@@ -192,20 +202,21 @@ macro_rules! options {
192202

193203
impl Options {
194204
/// Set an option and run its validator for its value.
195-
fn set_by_string_inner(&mut self, s: &str, val: &str) -> bool {
205+
fn set_by_string_inner(&mut self, s: &str, val: &str) -> Result<(), SetOptionByStringError> {
196206
match s {
197207
// Parse the given value from str (by env vars or by calling process()) to the right type
198-
$(stringify!($name) => if let Ok(typed_val) = val.parse::<$type>() {
199-
let is_set = self.$name.set(typed_val);
200-
if !is_set {
201-
eprintln!("Warn: unable to set {}={:?}. Invalid value. Default value will be used.", s, val);
208+
$(stringify!($name) => {
209+
let Ok(typed_val) = val.parse::<$type>() else {
210+
return Err(SetOptionByStringError::ValueParseError);
211+
};
212+
213+
if !self.$name.set(typed_val) {
214+
return Err(SetOptionByStringError::ValueValidationError);
202215
}
203-
is_set
204-
} else {
205-
eprintln!("Warn: unable to set {}={:?}. Can't parse value. Default value will be used.", s, val);
206-
false
216+
217+
Ok(())
207218
})*
208-
_ => panic!("Invalid Options key: {}", s)
219+
_ => Err(SetOptionByStringError::InvalidKey)
209220
}
210221
}
211222

@@ -250,31 +261,42 @@ impl Options {
250261
/// * `s`: The name of the option, same as the field name.
251262
/// * `val`: The value of the option, as a string. It will be parsed by `FromStr::from_str`.
252263
pub fn set_by_string(&mut self, s: &str, val: &str) -> bool {
253-
self.set_by_string_inner(s, val)
264+
self.set_by_string_inner(s, val).is_ok()
254265
}
255266

256267
/// Set options in bulk by names and values as strings.
257268
///
258269
/// Returns true if all the options are set successfully.
259270
///
260-
/// This method returns false if the option string is invalid, or if it includes any invalid
261-
/// option. If error happens, some options in `self` may have been modified while others are
262-
/// not.
271+
/// Panics if the `options` argument contains any unrecognized keys. Returns false if any
272+
/// option given in the `options` argument cannot be set due to parsing errors or validation
273+
/// errors.
263274
///
264275
/// Arguments:
265276
/// * `options`: a string that is key value pairs separated by white spaces or commas, e.g.
266277
/// `threads=1 stress_factor=4096`, or `threads=1,stress_factor=4096`. Each key-value pair
267278
/// will be set via [`Options::set_by_string`].
268279
pub fn bulk_set_by_string(&mut self, options: &str) -> bool {
269-
for opt in options.replace(",", " ").split_ascii_whitespace() {
280+
for opt in options.replace(',', " ").split_ascii_whitespace() {
270281
let kv_pair: Vec<&str> = opt.split('=').collect();
271282
if kv_pair.len() != 2 {
272283
return false;
273284
}
274285

275286
let key = kv_pair[0];
276287
let val = kv_pair[1];
277-
if !self.set_by_string(key, val) {
288+
if let Err(e) = self.set_by_string_inner(key, val) {
289+
match e {
290+
SetOptionByStringError::InvalidKey => {
291+
panic!("Invalid Options key: {}", key);
292+
}
293+
SetOptionByStringError::ValueParseError => {
294+
eprintln!("Warn: unable to set {}={:?}. Can't parse value. Default value will be used.", key, val);
295+
}
296+
SetOptionByStringError::ValueValidationError => {
297+
eprintln!("Warn: unable to set {}={:?}. Invalid value. Default value will be used.", key, val);
298+
}
299+
}
278300
return false;
279301
}
280302
}
@@ -292,7 +314,19 @@ impl Options {
292314
// strip the prefix, and get the lower case string
293315
if let Some(rest_of_key) = key.strip_prefix(PREFIX) {
294316
let lowercase: &str = &rest_of_key.to_lowercase();
295-
self.set_by_string(lowercase, &val);
317+
if let Err(e) = self.set_by_string_inner(lowercase, &val) {
318+
match e {
319+
SetOptionByStringError::InvalidKey => {
320+
/* Silently skip unrecognized keys. */
321+
}
322+
SetOptionByStringError::ValueParseError => {
323+
eprintln!("Warn: unable to set {}={:?}. Can't parse value. Default value will be used.", key, val);
324+
}
325+
SetOptionByStringError::ValueValidationError => {
326+
eprintln!("Warn: unable to set {}={:?}. Invalid value. Default value will be used.", key, val);
327+
}
328+
}
329+
}
296330
}
297331
}
298332
}

0 commit comments

Comments
 (0)