xref: /cloud-hypervisor/option_parser/src/lib.rs (revision 192b19c0607c87b246493deb193f9ca5d9f4682b)
1b69f6d4fSRob Bradford // Copyright © 2020 Intel Corporation
2b69f6d4fSRob Bradford //
3b69f6d4fSRob Bradford // SPDX-License-Identifier: Apache-2.0
4b69f6d4fSRob Bradford //
5b69f6d4fSRob Bradford 
6b69f6d4fSRob Bradford use std::collections::HashMap;
7ad521fd4SSebastien Boeuf use std::num::ParseIntError;
8b69f6d4fSRob Bradford use std::str::FromStr;
9b69f6d4fSRob Bradford 
100e40a504SPhilipp Schuster use thiserror::Error;
110e40a504SPhilipp Schuster 
12b69f6d4fSRob Bradford #[derive(Default)]
13b69f6d4fSRob Bradford pub struct OptionParser {
14b69f6d4fSRob Bradford     options: HashMap<String, OptionParserValue>,
15b69f6d4fSRob Bradford }
16b69f6d4fSRob Bradford 
17b69f6d4fSRob Bradford struct OptionParserValue {
18b69f6d4fSRob Bradford     value: Option<String>,
19b69f6d4fSRob Bradford     requires_value: bool,
20b69f6d4fSRob Bradford }
21b69f6d4fSRob Bradford 
220e40a504SPhilipp Schuster #[derive(Error, Debug)]
23b69f6d4fSRob Bradford pub enum OptionParserError {
240e40a504SPhilipp Schuster     #[error("unknown option: {0}")]
25b69f6d4fSRob Bradford     UnknownOption(String),
260e40a504SPhilipp Schuster     #[error("unknown option: {0}")]
27b69f6d4fSRob Bradford     InvalidSyntax(String),
280e40a504SPhilipp Schuster     #[error("unable to convert {1} for {0}")]
290e40a504SPhilipp Schuster     Conversion(String /* field */, String /* value */),
300e40a504SPhilipp Schuster     #[error("invalid value: {0}")]
311d89f98eSPraveen K Paladugu     InvalidValue(String),
32b69f6d4fSRob Bradford }
33b69f6d4fSRob Bradford type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
34b69f6d4fSRob Bradford 
split_commas(s: &str) -> OptionParserResult<Vec<String>>3553162147SRob Bradford fn split_commas(s: &str) -> OptionParserResult<Vec<String>> {
36a4f5ad60SSebastien Boeuf     let mut list: Vec<String> = Vec::new();
3746b790b5SRob Bradford     let mut opened_brackets = 0;
3871a7d5d8SRob Bradford     let mut in_quotes = false;
3946b790b5SRob Bradford     let mut current = String::new();
40a4f5ad60SSebastien Boeuf 
4146b790b5SRob Bradford     for c in s.trim().chars() {
4246b790b5SRob Bradford         match c {
4346b790b5SRob Bradford             '[' => {
4446b790b5SRob Bradford                 opened_brackets += 1;
4546b790b5SRob Bradford                 current.push('[');
46a4f5ad60SSebastien Boeuf             }
4746b790b5SRob Bradford             ']' => {
4846b790b5SRob Bradford                 opened_brackets -= 1;
4946b790b5SRob Bradford                 if opened_brackets < 0 {
5046b790b5SRob Bradford                     return Err(OptionParserError::InvalidSyntax(s.to_owned()));
5146b790b5SRob Bradford                 }
5246b790b5SRob Bradford                 current.push(']');
5346b790b5SRob Bradford             }
5471a7d5d8SRob Bradford             '"' => in_quotes = !in_quotes,
5546b790b5SRob Bradford             ',' => {
5671a7d5d8SRob Bradford                 if opened_brackets > 0 || in_quotes {
5746b790b5SRob Bradford                     current.push(',')
5846b790b5SRob Bradford                 } else {
5946b790b5SRob Bradford                     list.push(current);
6046b790b5SRob Bradford                     current = String::new();
6146b790b5SRob Bradford                 }
6246b790b5SRob Bradford             }
6346b790b5SRob Bradford             c => current.push(c),
6446b790b5SRob Bradford         }
6546b790b5SRob Bradford     }
6646b790b5SRob Bradford     list.push(current);
6746b790b5SRob Bradford 
6871a7d5d8SRob Bradford     if opened_brackets != 0 || in_quotes {
6946b790b5SRob Bradford         return Err(OptionParserError::InvalidSyntax(s.to_owned()));
70a4f5ad60SSebastien Boeuf     }
71a4f5ad60SSebastien Boeuf 
72a4f5ad60SSebastien Boeuf     Ok(list)
73a4f5ad60SSebastien Boeuf }
74a4f5ad60SSebastien Boeuf 
75b69f6d4fSRob Bradford impl OptionParser {
new() -> Self76b69f6d4fSRob Bradford     pub fn new() -> Self {
77b69f6d4fSRob Bradford         Self {
78b69f6d4fSRob Bradford             options: HashMap::new(),
79b69f6d4fSRob Bradford         }
80b69f6d4fSRob Bradford     }
81b69f6d4fSRob Bradford 
parse(&mut self, input: &str) -> OptionParserResult<()>82b69f6d4fSRob Bradford     pub fn parse(&mut self, input: &str) -> OptionParserResult<()> {
83b69f6d4fSRob Bradford         if input.trim().is_empty() {
84b69f6d4fSRob Bradford             return Ok(());
85b69f6d4fSRob Bradford         }
86b69f6d4fSRob Bradford 
8753162147SRob Bradford         for option in split_commas(input)?.iter() {
886ccf0379SRob Bradford             let parts: Vec<&str> = option.splitn(2, '=').collect();
89b69f6d4fSRob Bradford 
90b69f6d4fSRob Bradford             match self.options.get_mut(parts[0]) {
91b69f6d4fSRob Bradford                 None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
92b69f6d4fSRob Bradford                 Some(value) => {
93b69f6d4fSRob Bradford                     if value.requires_value {
94b69f6d4fSRob Bradford                         if parts.len() != 2 {
95b69f6d4fSRob Bradford                             return Err(OptionParserError::InvalidSyntax((*option).to_owned()));
96b69f6d4fSRob Bradford                         }
97b69f6d4fSRob Bradford                         value.value = Some(parts[1].trim().to_owned());
98b69f6d4fSRob Bradford                     } else {
99b69f6d4fSRob Bradford                         value.value = Some(String::new());
100b69f6d4fSRob Bradford                     }
101b69f6d4fSRob Bradford                 }
102b69f6d4fSRob Bradford             }
103b69f6d4fSRob Bradford         }
104b69f6d4fSRob Bradford 
105b69f6d4fSRob Bradford         Ok(())
106b69f6d4fSRob Bradford     }
107b69f6d4fSRob Bradford 
add(&mut self, option: &str) -> &mut Self108b69f6d4fSRob Bradford     pub fn add(&mut self, option: &str) -> &mut Self {
109b69f6d4fSRob Bradford         self.options.insert(
110b69f6d4fSRob Bradford             option.to_owned(),
111b69f6d4fSRob Bradford             OptionParserValue {
112b69f6d4fSRob Bradford                 value: None,
113b69f6d4fSRob Bradford                 requires_value: true,
114b69f6d4fSRob Bradford             },
115b69f6d4fSRob Bradford         );
116b69f6d4fSRob Bradford 
117b69f6d4fSRob Bradford         self
118b69f6d4fSRob Bradford     }
119b69f6d4fSRob Bradford 
add_valueless(&mut self, option: &str) -> &mut Self120b69f6d4fSRob Bradford     pub fn add_valueless(&mut self, option: &str) -> &mut Self {
121b69f6d4fSRob Bradford         self.options.insert(
122b69f6d4fSRob Bradford             option.to_owned(),
123b69f6d4fSRob Bradford             OptionParserValue {
124b69f6d4fSRob Bradford                 value: None,
125b69f6d4fSRob Bradford                 requires_value: false,
126b69f6d4fSRob Bradford             },
127b69f6d4fSRob Bradford         );
128b69f6d4fSRob Bradford 
129b69f6d4fSRob Bradford         self
130b69f6d4fSRob Bradford     }
131b69f6d4fSRob Bradford 
get(&self, option: &str) -> Option<String>132b69f6d4fSRob Bradford     pub fn get(&self, option: &str) -> Option<String> {
133b69f6d4fSRob Bradford         self.options
134b69f6d4fSRob Bradford             .get(option)
135b69f6d4fSRob Bradford             .and_then(|v| v.value.clone())
136b69f6d4fSRob Bradford             .and_then(|s| if s.is_empty() { None } else { Some(s) })
137b69f6d4fSRob Bradford     }
138b69f6d4fSRob Bradford 
is_set(&self, option: &str) -> bool139b69f6d4fSRob Bradford     pub fn is_set(&self, option: &str) -> bool {
140b69f6d4fSRob Bradford         self.options
141b69f6d4fSRob Bradford             .get(option)
142b69f6d4fSRob Bradford             .and_then(|v| v.value.as_ref())
143b69f6d4fSRob Bradford             .is_some()
144b69f6d4fSRob Bradford     }
145b69f6d4fSRob Bradford 
convert<T: FromStr>(&self, option: &str) -> OptionParserResult<Option<T>>146b69f6d4fSRob Bradford     pub fn convert<T: FromStr>(&self, option: &str) -> OptionParserResult<Option<T>> {
147b69f6d4fSRob Bradford         match self.get(option) {
148b69f6d4fSRob Bradford             None => Ok(None),
149b69f6d4fSRob Bradford             Some(v) => Ok(Some(v.parse().map_err(|_| {
150b69f6d4fSRob Bradford                 OptionParserError::Conversion(option.to_owned(), v.to_owned())
151b69f6d4fSRob Bradford             })?)),
152b69f6d4fSRob Bradford         }
153b69f6d4fSRob Bradford     }
154b69f6d4fSRob Bradford }
155b69f6d4fSRob Bradford 
156b69f6d4fSRob Bradford pub struct Toggle(pub bool);
157b69f6d4fSRob Bradford 
1587585e16fSPhilipp Schuster #[derive(Error, Debug)]
159b69f6d4fSRob Bradford pub enum ToggleParseError {
1607585e16fSPhilipp Schuster     #[error("invalid value: {0}")]
161b69f6d4fSRob Bradford     InvalidValue(String),
162b69f6d4fSRob Bradford }
163b69f6d4fSRob Bradford 
164b69f6d4fSRob Bradford impl FromStr for Toggle {
165b69f6d4fSRob Bradford     type Err = ToggleParseError;
166b69f6d4fSRob Bradford 
from_str(s: &str) -> std::result::Result<Self, Self::Err>167b69f6d4fSRob Bradford     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
168b69f6d4fSRob Bradford         match s.to_lowercase().as_str() {
169b69f6d4fSRob Bradford             "" => Ok(Toggle(false)),
170b69f6d4fSRob Bradford             "on" => Ok(Toggle(true)),
171b69f6d4fSRob Bradford             "off" => Ok(Toggle(false)),
172b69f6d4fSRob Bradford             "true" => Ok(Toggle(true)),
173b69f6d4fSRob Bradford             "false" => Ok(Toggle(false)),
174b69f6d4fSRob Bradford             _ => Err(ToggleParseError::InvalidValue(s.to_owned())),
175b69f6d4fSRob Bradford         }
176b69f6d4fSRob Bradford     }
177b69f6d4fSRob Bradford }
178b69f6d4fSRob Bradford 
179b69f6d4fSRob Bradford pub struct ByteSized(pub u64);
180b69f6d4fSRob Bradford 
1817585e16fSPhilipp Schuster #[derive(Error, Debug)]
182b69f6d4fSRob Bradford pub enum ByteSizedParseError {
1837585e16fSPhilipp Schuster     #[error("invalid value: {0}")]
184b69f6d4fSRob Bradford     InvalidValue(String),
185b69f6d4fSRob Bradford }
186b69f6d4fSRob Bradford 
187b69f6d4fSRob Bradford impl FromStr for ByteSized {
188b69f6d4fSRob Bradford     type Err = ByteSizedParseError;
189b69f6d4fSRob Bradford 
from_str(s: &str) -> std::result::Result<Self, Self::Err>190b69f6d4fSRob Bradford     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
191b69f6d4fSRob Bradford         Ok(ByteSized({
192b69f6d4fSRob Bradford             let s = s.trim();
193b69f6d4fSRob Bradford             let shift = if s.ends_with('K') {
194b69f6d4fSRob Bradford                 10
195b69f6d4fSRob Bradford             } else if s.ends_with('M') {
196b69f6d4fSRob Bradford                 20
197b69f6d4fSRob Bradford             } else if s.ends_with('G') {
198b69f6d4fSRob Bradford                 30
199b69f6d4fSRob Bradford             } else {
200b69f6d4fSRob Bradford                 0
201b69f6d4fSRob Bradford             };
202b69f6d4fSRob Bradford 
20361a5bae2SWei Liu             let s = s.trim_end_matches(['K', 'M', 'G']);
204b69f6d4fSRob Bradford             s.parse::<u64>()
205b69f6d4fSRob Bradford                 .map_err(|_| ByteSizedParseError::InvalidValue(s.to_owned()))?
206b69f6d4fSRob Bradford                 << shift
207b69f6d4fSRob Bradford         }))
208b69f6d4fSRob Bradford     }
209b69f6d4fSRob Bradford }
21042f963d6SSebastien Boeuf 
21142f963d6SSebastien Boeuf pub struct IntegerList(pub Vec<u64>);
21242f963d6SSebastien Boeuf 
2137585e16fSPhilipp Schuster #[derive(Error, Debug)]
21442f963d6SSebastien Boeuf pub enum IntegerListParseError {
2157585e16fSPhilipp Schuster     #[error("invalid value: {0}")]
21642f963d6SSebastien Boeuf     InvalidValue(String),
21742f963d6SSebastien Boeuf }
21842f963d6SSebastien Boeuf 
21942f963d6SSebastien Boeuf impl FromStr for IntegerList {
22042f963d6SSebastien Boeuf     type Err = IntegerListParseError;
22142f963d6SSebastien Boeuf 
from_str(s: &str) -> std::result::Result<Self, Self::Err>22242f963d6SSebastien Boeuf     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
22342f963d6SSebastien Boeuf         let mut integer_list = Vec::new();
224b81d758cSSebastien Boeuf         let ranges_list: Vec<&str> = s
225b81d758cSSebastien Boeuf             .trim()
226b81d758cSSebastien Boeuf             .trim_matches(|c| c == '[' || c == ']')
227b81d758cSSebastien Boeuf             .split(',')
228b81d758cSSebastien Boeuf             .collect();
22942f963d6SSebastien Boeuf 
23042f963d6SSebastien Boeuf         for range in ranges_list.iter() {
23142f963d6SSebastien Boeuf             let items: Vec<&str> = range.split('-').collect();
23242f963d6SSebastien Boeuf 
23342f963d6SSebastien Boeuf             if items.len() > 2 {
2346ebeaa92SPraveen Paladugu                 return Err(IntegerListParseError::InvalidValue((*range).to_string()));
23542f963d6SSebastien Boeuf             }
23642f963d6SSebastien Boeuf 
23742f963d6SSebastien Boeuf             let start_range = items[0]
23842f963d6SSebastien Boeuf                 .parse::<u64>()
23942f963d6SSebastien Boeuf                 .map_err(|_| IntegerListParseError::InvalidValue(items[0].to_owned()))?;
24042f963d6SSebastien Boeuf 
24142f963d6SSebastien Boeuf             integer_list.push(start_range);
24242f963d6SSebastien Boeuf 
24342f963d6SSebastien Boeuf             if items.len() == 2 {
24442f963d6SSebastien Boeuf                 let end_range = items[1]
24542f963d6SSebastien Boeuf                     .parse::<u64>()
24642f963d6SSebastien Boeuf                     .map_err(|_| IntegerListParseError::InvalidValue(items[1].to_owned()))?;
24742f963d6SSebastien Boeuf                 if start_range >= end_range {
2486ebeaa92SPraveen Paladugu                     return Err(IntegerListParseError::InvalidValue((*range).to_string()));
24942f963d6SSebastien Boeuf                 }
25042f963d6SSebastien Boeuf 
25142f963d6SSebastien Boeuf                 for i in start_range..end_range {
25242f963d6SSebastien Boeuf                     integer_list.push(i + 1);
25342f963d6SSebastien Boeuf                 }
25442f963d6SSebastien Boeuf             }
25542f963d6SSebastien Boeuf         }
25642f963d6SSebastien Boeuf 
25742f963d6SSebastien Boeuf         Ok(IntegerList(integer_list))
25842f963d6SSebastien Boeuf     }
25942f963d6SSebastien Boeuf }
260a5a29134SSebastien Boeuf 
261ad521fd4SSebastien Boeuf pub trait TupleValue {
parse_value(input: &str) -> Result<Self, TupleError> where Self: Sized262a4f5ad60SSebastien Boeuf     fn parse_value(input: &str) -> Result<Self, TupleError>
263ad521fd4SSebastien Boeuf     where
264ad521fd4SSebastien Boeuf         Self: Sized;
265ad521fd4SSebastien Boeuf }
266a5a29134SSebastien Boeuf 
267ad521fd4SSebastien Boeuf impl TupleValue for u64 {
parse_value(input: &str) -> Result<Self, TupleError>268a4f5ad60SSebastien Boeuf     fn parse_value(input: &str) -> Result<Self, TupleError> {
269a4f5ad60SSebastien Boeuf         input.parse::<u64>().map_err(TupleError::InvalidInteger)
270a4f5ad60SSebastien Boeuf     }
271a4f5ad60SSebastien Boeuf }
272a4f5ad60SSebastien Boeuf 
273a4f5ad60SSebastien Boeuf impl TupleValue for Vec<u8> {
parse_value(input: &str) -> Result<Self, TupleError>274a4f5ad60SSebastien Boeuf     fn parse_value(input: &str) -> Result<Self, TupleError> {
275a4f5ad60SSebastien Boeuf         Ok(IntegerList::from_str(input)
276a4f5ad60SSebastien Boeuf             .map_err(TupleError::InvalidIntegerList)?
277a4f5ad60SSebastien Boeuf             .0
278a4f5ad60SSebastien Boeuf             .iter()
279a4f5ad60SSebastien Boeuf             .map(|v| *v as u8)
280a4f5ad60SSebastien Boeuf             .collect())
281ad521fd4SSebastien Boeuf     }
282ad521fd4SSebastien Boeuf }
283ad521fd4SSebastien Boeuf 
284ad521fd4SSebastien Boeuf impl TupleValue for Vec<u64> {
parse_value(input: &str) -> Result<Self, TupleError>285a4f5ad60SSebastien Boeuf     fn parse_value(input: &str) -> Result<Self, TupleError> {
286a4f5ad60SSebastien Boeuf         Ok(IntegerList::from_str(input)
287a4f5ad60SSebastien Boeuf             .map_err(TupleError::InvalidIntegerList)?
288a4f5ad60SSebastien Boeuf             .0)
289ad521fd4SSebastien Boeuf     }
290ad521fd4SSebastien Boeuf }
291ad521fd4SSebastien Boeuf 
292e3327947SSean Banko impl TupleValue for Vec<usize> {
parse_value(input: &str) -> Result<Self, TupleError>293e3327947SSean Banko     fn parse_value(input: &str) -> Result<Self, TupleError> {
294e3327947SSean Banko         Ok(IntegerList::from_str(input)
295e3327947SSean Banko             .map_err(TupleError::InvalidIntegerList)?
296e3327947SSean Banko             .0
297e3327947SSean Banko             .iter()
298e3327947SSean Banko             .map(|v| *v as usize)
299e3327947SSean Banko             .collect())
300e3327947SSean Banko     }
301e3327947SSean Banko }
302e3327947SSean Banko 
303a4f5ad60SSebastien Boeuf pub struct Tuple<S, T>(pub Vec<(S, T)>);
304ad521fd4SSebastien Boeuf 
3057585e16fSPhilipp Schuster #[derive(Error, Debug)]
306ad521fd4SSebastien Boeuf pub enum TupleError {
3077585e16fSPhilipp Schuster     #[error("invalid value: {0}")]
308a5a29134SSebastien Boeuf     InvalidValue(String),
309*192b19c0SPhilipp Schuster     #[error("split outside brackets")]
3107585e16fSPhilipp Schuster     SplitOutsideBrackets(#[source] OptionParserError),
311*192b19c0SPhilipp Schuster     #[error("invalid integer list")]
3127585e16fSPhilipp Schuster     InvalidIntegerList(#[source] IntegerListParseError),
313*192b19c0SPhilipp Schuster     #[error("invalid integer")]
3147585e16fSPhilipp Schuster     InvalidInteger(#[source] ParseIntError),
315a5a29134SSebastien Boeuf }
316a5a29134SSebastien Boeuf 
317a4f5ad60SSebastien Boeuf impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
318ad521fd4SSebastien Boeuf     type Err = TupleError;
319a5a29134SSebastien Boeuf 
from_str(s: &str) -> std::result::Result<Self, Self::Err>320a5a29134SSebastien Boeuf     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
321a4f5ad60SSebastien Boeuf         let mut list: Vec<(S, T)> = Vec::new();
322a5a29134SSebastien Boeuf 
3239f89e0a4SRob Bradford         let body = s
3249f89e0a4SRob Bradford             .trim()
3259f89e0a4SRob Bradford             .strip_prefix('[')
3269f89e0a4SRob Bradford             .and_then(|s| s.strip_suffix(']'))
3279f89e0a4SRob Bradford             .ok_or_else(|| TupleError::InvalidValue(s.to_string()))?;
3289f89e0a4SRob Bradford 
3299f89e0a4SRob Bradford         let tuples_list = split_commas(body).map_err(TupleError::SplitOutsideBrackets)?;
330a5a29134SSebastien Boeuf         for tuple in tuples_list.iter() {
331a5a29134SSebastien Boeuf             let items: Vec<&str> = tuple.split('@').collect();
332a5a29134SSebastien Boeuf 
333a5a29134SSebastien Boeuf             if items.len() != 2 {
334ad521fd4SSebastien Boeuf                 return Err(TupleError::InvalidValue((*tuple).to_string()));
335a5a29134SSebastien Boeuf             }
336a5a29134SSebastien Boeuf 
337a5a29134SSebastien Boeuf             let item1 = items[0]
338a4f5ad60SSebastien Boeuf                 .parse::<S>()
339ad521fd4SSebastien Boeuf                 .map_err(|_| TupleError::InvalidValue(items[0].to_owned()))?;
340a4f5ad60SSebastien Boeuf             let item2 = TupleValue::parse_value(items[1])?;
341a5a29134SSebastien Boeuf 
342a5a29134SSebastien Boeuf             list.push((item1, item2));
343a5a29134SSebastien Boeuf         }
344a5a29134SSebastien Boeuf 
345ad521fd4SSebastien Boeuf         Ok(Tuple(list))
346a5a29134SSebastien Boeuf     }
347a5a29134SSebastien Boeuf }
348dc423243SSebastien Boeuf 
3496b0df31eSWilliam Douglas #[derive(Default)]
350dc423243SSebastien Boeuf pub struct StringList(pub Vec<String>);
351dc423243SSebastien Boeuf 
3527585e16fSPhilipp Schuster #[derive(Error, Debug)]
353dc423243SSebastien Boeuf pub enum StringListParseError {
3547585e16fSPhilipp Schuster     #[error("invalid value: {0}")]
355dc423243SSebastien Boeuf     InvalidValue(String),
356dc423243SSebastien Boeuf }
357dc423243SSebastien Boeuf 
358dc423243SSebastien Boeuf impl FromStr for StringList {
359dc423243SSebastien Boeuf     type Err = StringListParseError;
360dc423243SSebastien Boeuf 
from_str(s: &str) -> std::result::Result<Self, Self::Err>361dc423243SSebastien Boeuf     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
362b81d758cSSebastien Boeuf         let string_list: Vec<String> = s
363b81d758cSSebastien Boeuf             .trim()
364b81d758cSSebastien Boeuf             .trim_matches(|c| c == '[' || c == ']')
365b81d758cSSebastien Boeuf             .split(',')
366b81d758cSSebastien Boeuf             .map(|e| e.to_owned())
367b81d758cSSebastien Boeuf             .collect();
368dc423243SSebastien Boeuf 
369dc423243SSebastien Boeuf         Ok(StringList(string_list))
370dc423243SSebastien Boeuf     }
371dc423243SSebastien Boeuf }
372d295de4cSRob Bradford 
373d295de4cSRob Bradford #[cfg(test)]
374d295de4cSRob Bradford mod tests {
375d295de4cSRob Bradford     use super::*;
376d295de4cSRob Bradford 
377d295de4cSRob Bradford     #[test]
test_option_parser()378d295de4cSRob Bradford     fn test_option_parser() {
379d295de4cSRob Bradford         let mut parser = OptionParser::new();
380d295de4cSRob Bradford         parser
381d295de4cSRob Bradford             .add("size")
382d295de4cSRob Bradford             .add("mergeable")
383d295de4cSRob Bradford             .add("hotplug_method")
38499904398SRob Bradford             .add("hotplug_size")
38571a7d5d8SRob Bradford             .add("topology")
38671a7d5d8SRob Bradford             .add("cmdline");
387d295de4cSRob Bradford 
388297236a7SRuoqing He         parser.parse("size=128M,hanging_param").unwrap_err();
389297236a7SRuoqing He         parser
390297236a7SRuoqing He             .parse("size=128M,too_many_equals=foo=bar")
391297236a7SRuoqing He             .unwrap_err();
392297236a7SRuoqing He         parser.parse("size=128M,file=/dev/shm").unwrap_err();
393d295de4cSRob Bradford 
394297236a7SRuoqing He         parser.parse("size=128M").unwrap();
395d295de4cSRob Bradford         assert_eq!(parser.get("size"), Some("128M".to_owned()));
396d295de4cSRob Bradford         assert!(!parser.is_set("mergeable"));
397d295de4cSRob Bradford         assert!(parser.is_set("size"));
39899904398SRob Bradford 
399297236a7SRuoqing He         parser.parse("size=128M,mergeable=on").unwrap();
40099904398SRob Bradford         assert_eq!(parser.get("size"), Some("128M".to_owned()));
40199904398SRob Bradford         assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
40299904398SRob Bradford 
403297236a7SRuoqing He         parser
40499904398SRob Bradford             .parse("size=128M,mergeable=on,topology=[1,2]")
405297236a7SRuoqing He             .unwrap();
40699904398SRob Bradford         assert_eq!(parser.get("size"), Some("128M".to_owned()));
40799904398SRob Bradford         assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
40899904398SRob Bradford         assert_eq!(parser.get("topology"), Some("[1,2]".to_owned()));
40999904398SRob Bradford 
410297236a7SRuoqing He         parser
41199904398SRob Bradford             .parse("size=128M,mergeable=on,topology=[[1,2],[3,4]]")
412297236a7SRuoqing He             .unwrap();
41399904398SRob Bradford         assert_eq!(parser.get("size"), Some("128M".to_owned()));
41499904398SRob Bradford         assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
41599904398SRob Bradford         assert_eq!(parser.get("topology"), Some("[[1,2],[3,4]]".to_owned()));
41699904398SRob Bradford 
417297236a7SRuoqing He         parser.parse("topology=[").unwrap_err();
418297236a7SRuoqing He         parser.parse("topology=[[[]]]]").unwrap_err();
41971a7d5d8SRob Bradford 
420297236a7SRuoqing He         parser.parse("cmdline=\"console=ttyS0,9600n8\"").unwrap();
42171a7d5d8SRob Bradford         assert_eq!(
42271a7d5d8SRob Bradford             parser.get("cmdline"),
42371a7d5d8SRob Bradford             Some("console=ttyS0,9600n8".to_owned())
42471a7d5d8SRob Bradford         );
425297236a7SRuoqing He         parser.parse("cmdline=\"").unwrap_err();
426297236a7SRuoqing He         parser.parse("cmdline=\"\"\"").unwrap_err();
427d295de4cSRob Bradford     }
428d295de4cSRob Bradford }
429