From a4f5ad607645f56ec0e157fca366475e59207f06 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Wed, 10 Nov 2021 10:34:25 +0100 Subject: [PATCH] option_parser: Fix inner bracket support with list of integers Give the option parser the ability to handle tuples with inner brackets containing list of integers. The following example can now be handled correctly "option=[key@[v1-v2,v3,v4]]" which means the option is assigned a tuple with a key associated with a list of integers between the range v1 - v2, as well as v3 and v4. Signed-off-by: Sebastien Boeuf --- option_parser/src/lib.rs | 99 +++++++++++++++++++++++----------------- vmm/src/config.rs | 2 +- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/option_parser/src/lib.rs b/option_parser/src/lib.rs index 86424ed95..327b982a8 100644 --- a/option_parser/src/lib.rs +++ b/option_parser/src/lib.rs @@ -38,6 +38,32 @@ impl fmt::Display for OptionParserError { } type OptionParserResult = std::result::Result; +fn split_commas_outside_brackets(s: &str) -> OptionParserResult> { + let mut list: Vec = Vec::new(); + let mut opened_brackets: usize = 0; + for element in s.trim().split(',') { + if opened_brackets > 0 { + if let Some(last) = list.last_mut() { + *last = format!("{},{}", last, element); + } else { + return Err(OptionParserError::InvalidSyntax(s.to_owned())); + } + } else { + list.push(element.to_string()); + } + + opened_brackets += element.matches('[').count(); + let closing_brackets = element.matches(']').count(); + if closing_brackets > opened_brackets { + return Err(OptionParserError::InvalidSyntax(s.to_owned())); + } else { + opened_brackets -= closing_brackets; + } + } + + Ok(list) +} + impl OptionParser { pub fn new() -> Self { Self { @@ -50,29 +76,7 @@ impl OptionParser { return Ok(()); } - let mut options_list: Vec = Vec::new(); - let mut opened_brackets: usize = 0; - for element in input.trim().split(',') { - if opened_brackets > 0 { - if let Some(last) = options_list.last_mut() { - *last = format!("{},{}", last, element); - } else { - return Err(OptionParserError::InvalidSyntax(input.to_owned())); - } - } else { - options_list.push(element.to_string()); - } - - opened_brackets += element.matches('[').count(); - let closing_brackets = element.matches(']').count(); - if closing_brackets > opened_brackets { - return Err(OptionParserError::InvalidSyntax(input.to_owned())); - } else { - opened_brackets -= closing_brackets; - } - } - - for option in options_list.iter() { + for option in split_commas_outside_brackets(input)?.iter() { let parts: Vec<&str> = option.split('=').collect(); match self.options.get_mut(parts[0]) { @@ -242,44 +246,54 @@ impl FromStr for IntegerList { } pub trait TupleValue { - fn parse_value(input: &str) -> Result + fn parse_value(input: &str) -> Result where Self: Sized; } impl TupleValue for u64 { - fn parse_value(input: &str) -> Result { - input.parse::() + fn parse_value(input: &str) -> Result { + input.parse::().map_err(TupleError::InvalidInteger) + } +} + +impl TupleValue for Vec { + fn parse_value(input: &str) -> Result { + Ok(IntegerList::from_str(input) + .map_err(TupleError::InvalidIntegerList)? + .0 + .iter() + .map(|v| *v as u8) + .collect()) } } impl TupleValue for Vec { - fn parse_value(input: &str) -> Result { - input - .trim_matches(|c| c == '[' || c == ']') - .split(',') - .map(|i| i.parse::()) - .collect::, _>>() + fn parse_value(input: &str) -> Result { + Ok(IntegerList::from_str(input) + .map_err(TupleError::InvalidIntegerList)? + .0) } } -pub struct Tuple(pub Vec<(u64, T)>); +pub struct Tuple(pub Vec<(S, T)>); pub enum TupleError { InvalidValue(String), + SplitOutsideBrackets(OptionParserError), + InvalidIntegerList(IntegerListParseError), + InvalidInteger(ParseIntError), } -impl FromStr for Tuple { +impl FromStr for Tuple { type Err = TupleError; fn from_str(s: &str) -> std::result::Result { - let mut list: Vec<(u64, T)> = Vec::new(); - let tuples_list: Vec<&str> = s - .trim() - .trim_matches(|c| c == '[' || c == ']') - .split(',') - .collect(); + let mut list: Vec<(S, T)> = Vec::new(); + let tuples_list = + split_commas_outside_brackets(s.trim().trim_matches(|c| c == '[' || c == ']')) + .map_err(TupleError::SplitOutsideBrackets)?; for tuple in tuples_list.iter() { let items: Vec<&str> = tuple.split('@').collect(); @@ -288,10 +302,9 @@ impl FromStr for Tuple { } let item1 = items[0] - .parse::() + .parse::() .map_err(|_| TupleError::InvalidValue(items[0].to_owned()))?; - let item2 = TupleValue::parse_value(items[1]) - .map_err(|_| TupleError::InvalidValue(items[1].to_owned()))?; + let item2 = TupleValue::parse_value(items[1])?; list.push((item1, item2)); } diff --git a/vmm/src/config.rs b/vmm/src/config.rs index ac0373ee6..ee3026884 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -1942,7 +1942,7 @@ impl NumaConfig { .map_err(Error::ParseNuma)? .map(|v| v.0.iter().map(|e| *e as u8).collect()); let distances = parser - .convert::>("distances") + .convert::>("distances") .map_err(Error::ParseNuma)? .map(|v| { v.0.iter()