//! As a rule target for options should provide a list of supported options and ignore unknown options.
use std::sync::Arc;
+use std::fmt;
/// A list specifying option parsing and validating errors.
#[derive(Clone,Copy,Debug,PartialEq)]
/// A specialised `Result` type for option parsing/validation.
pub type OptionResult<T> = Result<T, OptionError>;
+/// Option definition type.
+#[derive(Debug)]
+pub enum NAOptionDefinitionType {
+ /// Option may just be present.
+ None,
+ /// Option is a boolean value.
+ Bool,
+ /// Option is an integer with optional minimum and maximum value.
+ Int(Option<i64>, Option<i64>),
+ /// Option is a floating point number with optional minimum and maximum value.
+ Float(Option<f64>, Option<f64>),
+ /// Option is a string with an optional list of allowed values.
+ String(Option<&'static [&'static str]>),
+ /// Option is some binary data.
+ Data,
+}
+
/// Option definition.
#[derive(Debug)]
pub struct NAOptionDefinition {
pub name: &'static str,
/// Option meaning.
pub description: &'static str,
- /// Minimal value for the option (if applicable).
- pub min_value: Option<NAValue>,
- /// Maximum value for the option (if applicable).
- pub max_value: Option<NAValue>,
- /// Allowed string values (when value is a string).
- pub valid_strings: Option<Vec<String>>,
- /// Default option value.
- ///
- /// This is used mainly to tell in which format options should be (e.g. bool or float).
- pub default_value: NAValue,
+ /// Option type.
+ pub opt_type: NAOptionDefinitionType,
}
impl NAOptionDefinition {
let no_name = "no".to_owned() + self.name;
let opt_no_name = "--no".to_owned() + self.name;
if name == &no_name || name == &opt_no_name {
- match self.default_value {
- NAValue::Bool(_) => return Ok((NAOption { name: self.name, value: NAValue::Bool(false) }, 1)),
+ match self.opt_type {
+ NAOptionDefinitionType::Bool => return Ok((NAOption { name: self.name, value: NAValue::Bool(false) }, 1)),
_ => return Err(OptionError::InvalidFormat),
};
}
if self.name != name && &opt_name != name {
return Err(OptionError::WrongName);
}
- match self.default_value {
- NAValue::None => Ok((NAOption { name: self.name, value: NAValue::None }, 1)),
- NAValue::Bool(_) => Ok((NAOption { name: self.name, value: NAValue::Bool(true) }, 1)),
- NAValue::Int(_) => {
- if let Some(str) = value {
- let ret = str.parse::<i32>();
- if let Ok(val) = ret {
- let opt = NAOption { name: self.name, value: NAValue::Int(val) };
- self.check(&opt)?;
- Ok((opt, 2))
- } else {
- Err(OptionError::ParseError)
- }
- } else {
- Err(OptionError::ParseError)
- }
- },
- NAValue::Long(_) => {
+ match self.opt_type {
+ NAOptionDefinitionType::None => Ok((NAOption { name: self.name, value: NAValue::None }, 1)),
+ NAOptionDefinitionType::Bool => Ok((NAOption { name: self.name, value: NAValue::Bool(true) }, 1)),
+ NAOptionDefinitionType::Int(_, _) => {
if let Some(str) = value {
let ret = str.parse::<i64>();
if let Ok(val) = ret {
- let opt = NAOption { name: self.name, value: NAValue::Long(val) };
+ let opt = NAOption { name: self.name, value: NAValue::Int(val) };
self.check(&opt)?;
Ok((opt, 2))
} else {
Err(OptionError::ParseError)
}
},
- NAValue::Float(_) => {
+ NAOptionDefinitionType::Float(_, _) => {
if let Some(str) = value {
let ret = str.parse::<f64>();
if let Ok(val) = ret {
Err(OptionError::ParseError)
}
},
- NAValue::String(_) => {
+ NAOptionDefinitionType::String(_) => {
if let Some(str) = value {
let opt = NAOption { name: self.name, value: NAValue::String(str.to_string()) };
self.check(&opt)?;
NAValue::None => Ok(()),
NAValue::Bool(_) => Ok(()),
NAValue::Int(intval) => {
- if let Some(ref minval) = self.min_value {
- let (minres, _) = Self::compare(i64::from(intval), minval)?;
- if !minres {
- return Err(OptionError::InvalidValue);
- }
- }
- if let Some(ref maxval) = self.max_value {
- let (_, maxres) = Self::compare(i64::from(intval), maxval)?;
- if !maxres {
- return Err(OptionError::InvalidValue);
- }
- }
- Ok(())
- },
- NAValue::Long(intval) => {
- if let Some(ref minval) = self.min_value {
- let (minres, _) = Self::compare(intval, minval)?;
- if !minres {
- return Err(OptionError::InvalidValue);
- }
- }
- if let Some(ref maxval) = self.max_value {
- let (_, maxres) = Self::compare(intval, maxval)?;
- if !maxres {
- return Err(OptionError::InvalidValue);
- }
- }
+ match self.opt_type {
+ NAOptionDefinitionType::Int(minval, maxval) => {
+ if let Some(minval) = minval {
+ if intval < minval { return Err(OptionError::InvalidValue); }
+ }
+ if let Some(maxval) = maxval {
+ if intval > maxval { return Err(OptionError::InvalidValue); }
+ }
+ },
+ NAOptionDefinitionType::Float(minval, maxval) => {
+ let fval = intval as f64;
+ if let Some(minval) = minval {
+ if fval < minval { return Err(OptionError::InvalidValue); }
+ }
+ if let Some(maxval) = maxval {
+ if fval > maxval { return Err(OptionError::InvalidValue); }
+ }
+ },
+ _ => return Err(OptionError::InvalidFormat),
+ };
Ok(())
},
NAValue::Float(fval) => {
- if let Some(ref minval) = self.min_value {
- let (minres, _) = Self::compare_f64(fval, minval)?;
- if !minres {
- return Err(OptionError::InvalidValue);
- }
- }
- if let Some(ref maxval) = self.max_value {
- let (_, maxres) = Self::compare_f64(fval, maxval)?;
- if !maxres {
- return Err(OptionError::InvalidValue);
- }
- }
+ match self.opt_type {
+ NAOptionDefinitionType::Int(minval, maxval) => {
+ let intval = fval as i64;
+ if let Some(minval) = minval {
+ if intval < minval { return Err(OptionError::InvalidValue); }
+ }
+ if let Some(maxval) = maxval {
+ if intval > maxval { return Err(OptionError::InvalidValue); }
+ }
+ },
+ NAOptionDefinitionType::Float(minval, maxval) => {
+ if let Some(minval) = minval {
+ if fval < minval { return Err(OptionError::InvalidValue); }
+ }
+ if let Some(maxval) = maxval {
+ if fval > maxval { return Err(OptionError::InvalidValue); }
+ }
+ },
+ _ => return Err(OptionError::InvalidFormat),
+ };
Ok(())
},
NAValue::String(ref cur_str) => {
- if let Some(ref strings) = self.valid_strings {
+ if let NAOptionDefinitionType::String(Some(ref strings)) = self.opt_type {
for str in strings.iter() {
if cur_str == str {
return Ok(());
NAValue::Data(_) => Ok(()),
}
}
- fn compare(val: i64, ref_val: &NAValue) -> OptionResult<(bool, bool)> {
- match ref_val {
- NAValue::Int(ref_min) => {
- Ok((val >= i64::from(*ref_min), val <= i64::from(*ref_min)))
- },
- NAValue::Long(ref_min) => {
- Ok((val >= *ref_min, val <= *ref_min))
- },
- NAValue::Float(ref_min) => {
- Ok(((val as f64) >= *ref_min, (val as f64) <= *ref_min))
- },
- _ => Err(OptionError::InvalidFormat),
- }
- }
- fn compare_f64(val: f64, ref_val: &NAValue) -> OptionResult<(bool, bool)> {
- match ref_val {
- NAValue::Int(ref_min) => {
- Ok((val >= f64::from(*ref_min), val <= f64::from(*ref_min)))
+}
+
+impl fmt::Display for NAOptionDefinition {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.opt_type {
+ NAOptionDefinitionType::None => write!(f, "{}: {}", self.name, self.description),
+ NAOptionDefinitionType::Bool => write!(f, "[no]{}: {}", self.name, self.description),
+ NAOptionDefinitionType::String(ref str_list) => {
+ if let Some(ref opts) = str_list {
+ write!(f, "{} {}: {}", self.name, opts.join("|"), self.description)
+ } else {
+ write!(f, "{} <string>: {}", self.name, self.description)
+ }
},
- NAValue::Long(ref_min) => {
- Ok((val >= (*ref_min as f64), val <= (*ref_min as f64)))
+ NAOptionDefinitionType::Int(minval, maxval) => {
+ let range = match (&minval, &maxval) {
+ (Some(minval), None) => format!("{}-..", minval),
+ (None, Some(maxval)) => format!("..-{}", maxval),
+ (Some(minval), Some(maxval)) => format!("{}-{}", minval, maxval),
+ _ => "<integer>".to_string(),
+ };
+ write!(f, "{} {}: {}", self.name, range, self.description)
},
- NAValue::Float(ref_min) => {
- Ok((val >= *ref_min, val <= *ref_min))
+ NAOptionDefinitionType::Float(minval, maxval) => {
+ let range = match (&minval, &maxval) {
+ (Some(minval), None) => format!("{}-..", minval),
+ (None, Some(maxval)) => format!("..-{}", maxval),
+ (Some(minval), Some(maxval)) => format!("{}-{}", minval, maxval),
+ _ => "<float>".to_string(),
+ };
+ write!(f, "{} {}: {}", self.name, range, self.description)
},
- _ => Err(OptionError::InvalidFormat),
+ NAOptionDefinitionType::Data => write!(f, "{} <binary data>: {}", self.name, self.description),
}
}
}
/// Boolean value.
Bool(bool),
/// Integer value.
- Int(i32),
- /// Long integer value.
- Long(i64),
+ Int(i64),
/// Floating point value.
Float(f64),
/// String value.
#[test]
fn test_option_validation() {
let option = NAOption {name: "option", value: NAValue::Int(42) };
- let mut def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::Float(0.0) };
+ let mut def = NAOptionDefinition { name: "option", description: "", opt_type: NAOptionDefinitionType::Int(None, None) };
assert!(def.check(&option).is_ok());
- def.max_value = Some(NAValue::Long(30));
+ def.opt_type = NAOptionDefinitionType::Int(None, Some(30));
assert_eq!(def.check(&option), Err(OptionError::InvalidValue));
- def.max_value = None;
- def.min_value = Some(NAValue::Int(40));
+ def.opt_type = NAOptionDefinitionType::Int(Some(40), None);
assert!(def.check(&option).is_ok());
def.name = "option2";
assert_eq!(def.check(&option), Err(OptionError::WrongName));
let option = NAOption {name: "option", value: NAValue::String("test".to_string()) };
- let mut def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::String("".to_string()) };
+ let mut def = NAOptionDefinition { name: "option", description: "", opt_type: NAOptionDefinitionType::String(None) };
assert!(def.check(&option).is_ok());
- def.valid_strings = Some(vec!["a string".to_string(), "test string".to_string()]);
+ def.opt_type = NAOptionDefinitionType::String(Some(&["a string", "test string"]));
assert_eq!(def.check(&option), Err(OptionError::InvalidValue));
- def.valid_strings = Some(vec!["a string".to_string(), "test".to_string()]);
+ def.opt_type = NAOptionDefinitionType::String(Some(&["a string", "test"]));
assert!(def.check(&option).is_ok());
}
#[test]
fn test_option_parsing() {
- let mut def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::Float(0.0) };
+ let mut def = NAOptionDefinition { name: "option", description: "", opt_type: NAOptionDefinitionType::Float(None, None) };
assert_eq!(def.parse(&"--option".to_string(), None), Err(OptionError::ParseError));
assert_eq!(def.parse(&"--nooption".to_string(), None), Err(OptionError::InvalidFormat));
assert_eq!(def.parse(&"--option".to_string(), Some(&"42".to_string())),
Ok((NAOption{name:"option",value: NAValue::Float(42.0)}, 2)));
- def.max_value = Some(NAValue::Float(40.0));
+ def.opt_type = NAOptionDefinitionType::Float(None, Some(40.0));
assert_eq!(def.parse(&"--option".to_string(), Some(&"42".to_string())),
Err(OptionError::InvalidValue));
- let def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::Bool(true) };
+ let def = NAOptionDefinition { name: "option", description: "", opt_type: NAOptionDefinitionType::Bool };
assert_eq!(def.parse(&"option".to_string(), None),
Ok((NAOption{name: "option", value: NAValue::Bool(true) }, 1)));
assert_eq!(def.parse(&"nooption".to_string(), None),