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