vmm: config: Introduce basic OptionParser

This will be used to simplify and consolidate much of the parsing code
used for command line parameters.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2020-03-27 11:15:55 +00:00
parent 1e20b5727d
commit 9e7231cd69

View File

@ -7,12 +7,14 @@ extern crate vm_virtio;
use clap::ArgMatches;
use net_util::MacAddr;
use std::collections::HashMap;
use std::convert::From;
use std::io;
use std::net::AddrParseError;
use std::net::Ipv4Addr;
use std::path::PathBuf;
use std::result;
use std::str::FromStr;
pub const DEFAULT_VCPUS: u8 = 1;
pub const DEFAULT_MEMORY_MB: u64 = 512;
@ -112,6 +114,84 @@ pub enum Error {
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Default)]
pub struct OptionParser {
options: HashMap<String, OptionParserValue>,
}
struct OptionParserValue {
value: Option<String>,
}
#[derive(Debug)]
pub enum OptionParserError {
UnknownOption(String),
InvalidSyntax(String),
Conversion(String, String),
}
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
impl OptionParser {
pub fn new() -> Self {
Self {
options: HashMap::new(),
}
}
pub fn parse(&mut self, input: &str) -> OptionParserResult<()> {
if input.trim().is_empty() {
return Ok(());
}
let options_list: Vec<&str> = input.trim().split(',').collect();
for option in options_list.iter() {
let parts: Vec<&str> = option.split('=').collect();
if parts.len() != 2 {
return Err(OptionParserError::InvalidSyntax((*option).to_owned()));
}
match self.options.get_mut(parts[0]) {
None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
Some(value) => {
value.value = Some(parts[1].trim().to_owned());
}
}
}
Ok(())
}
pub fn add(&mut self, option: &str) -> &mut Self {
self.options
.insert(option.to_owned(), OptionParserValue { value: None });
self
}
pub fn get(&self, option: &str) -> Option<String> {
self.options.get(option).and_then(|v| v.value.clone())
}
pub fn is_set(&self, option: &str) -> bool {
self.options
.get(option)
.and_then(|v| v.value.as_ref())
.is_some()
}
pub fn convert<T: FromStr>(&self, option: &str) -> OptionParserResult<Option<T>> {
match self.options.get(option).and_then(|v| v.value.as_ref()) {
None => Ok(None),
Some(v) => Ok(Some(v.parse().map_err(|_| {
OptionParserError::Conversion(option.to_owned(), v.to_owned())
})?)),
}
}
}
pub struct VmParams<'a> {
pub cpus: &'a str,
pub memory: &'a str,
@ -1187,3 +1267,33 @@ impl VmConfig {
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_option_parser() -> std::result::Result<(), OptionParserError> {
let mut parser = OptionParser::new();
parser
.add("size")
.add("file")
.add("mergeable")
.add("hotplug_method")
.add("hotplug_size");
assert!(parser
.parse("size=128M,file=/dev/shm,hanging_param")
.is_err());
assert!(parser
.parse("size=128M,file=/dev/shm,too_many_equals=foo=bar")
.is_err());
assert!(parser.parse("size=128M,file=/dev/shm").is_ok());
assert_eq!(parser.get("size"), Some("128M".to_owned()));
assert_eq!(parser.get("file"), Some("/dev/shm".to_owned()));
assert!(!parser.is_set("mergeable"));
assert!(parser.is_set("size"));
Ok(())
}
}