3 //! This module contains the definitions for options and names for common options.
4 //! Options are used to set custom parameters in e.g. decoders or muxers.
6 //! As a rule target for options should provide a list of supported options and ignore unknown options.
10 /// A list specifying option parsing and validating errors.
11 #[derive(Clone,Copy,Debug,PartialEq)]
12 pub enum OptionError {
13 /// Input is not intended for the current option definition.
15 /// Option value is not in the expected format.
17 /// Option value was not in the range.
23 /// A specialised `Result` type for option parsing/validation.
24 pub type OptionResult<T> = Result<T, OptionError>;
26 /// Option definition.
28 pub struct NAOptionDefinition {
30 pub name: &'static str,
32 pub description: &'static str,
33 /// Minimal value for the option (if applicable).
34 pub min_value: Option<NAValue>,
35 /// Maximum value for the option (if applicable).
36 pub max_value: Option<NAValue>,
37 /// Allowed string values (when value is a string).
38 pub valid_strings: Option<Vec<String>>,
39 /// Default option value.
41 /// This is used mainly to tell in which format options should be (e.g. bool or float).
42 pub default_value: NAValue,
45 impl NAOptionDefinition {
46 /// Tries to parse input string(s) as an option and returns new option and number of arguments used (1 or 2) on success.
47 pub fn parse(&self, name: &String, value: Option<&String>) -> OptionResult<(NAOption, usize)> {
48 let no_name = "no".to_owned() + self.name;
49 let opt_no_name = "--no".to_owned() + self.name;
50 if name == &no_name || name == &opt_no_name {
51 match self.default_value {
52 NAValue::Bool(_) => return Ok((NAOption { name: self.name, value: NAValue::Bool(false) }, 1)),
53 _ => return Err(OptionError::InvalidFormat),
56 let opt_name = "--".to_owned() + self.name;
57 if self.name != name && &opt_name != name {
58 return Err(OptionError::WrongName);
60 match self.default_value {
61 NAValue::None => Ok((NAOption { name: self.name, value: NAValue::None }, 1)),
62 NAValue::Bool(_) => Ok((NAOption { name: self.name, value: NAValue::Bool(true) }, 1)),
64 if let Some(str) = value {
65 let ret = str.parse::<i32>();
66 if let Ok(val) = ret {
67 let opt = NAOption { name: self.name, value: NAValue::Int(val) };
71 Err(OptionError::ParseError)
74 Err(OptionError::ParseError)
78 if let Some(str) = value {
79 let ret = str.parse::<i64>();
80 if let Ok(val) = ret {
81 let opt = NAOption { name: self.name, value: NAValue::Long(val) };
85 Err(OptionError::ParseError)
88 Err(OptionError::ParseError)
91 NAValue::Float(_) => {
92 if let Some(str) = value {
93 let ret = str.parse::<f64>();
94 if let Ok(val) = ret {
95 let opt = NAOption { name: self.name, value: NAValue::Float(val) };
99 Err(OptionError::ParseError)
102 Err(OptionError::ParseError)
105 NAValue::String(_) => {
106 if let Some(str) = value {
107 let opt = NAOption { name: self.name, value: NAValue::String(str.to_string()) };
111 Err(OptionError::ParseError)
114 _ => unimplemented!(),
117 /// Checks whether input option conforms to the definition i.e. whether it has proper format and it lies in range.
118 pub fn check(&self, option: &NAOption) -> OptionResult<()> {
119 if option.name != self.name {
120 return Err(OptionError::WrongName);
123 NAValue::None => Ok(()),
124 NAValue::Bool(_) => Ok(()),
125 NAValue::Int(intval) => {
126 if let Some(ref minval) = self.min_value {
127 let (minres, _) = Self::compare(i64::from(intval), minval)?;
129 return Err(OptionError::InvalidValue);
132 if let Some(ref maxval) = self.max_value {
133 let (_, maxres) = Self::compare(i64::from(intval), maxval)?;
135 return Err(OptionError::InvalidValue);
140 NAValue::Long(intval) => {
141 if let Some(ref minval) = self.min_value {
142 let (minres, _) = Self::compare(intval, minval)?;
144 return Err(OptionError::InvalidValue);
147 if let Some(ref maxval) = self.max_value {
148 let (_, maxres) = Self::compare(intval, maxval)?;
150 return Err(OptionError::InvalidValue);
155 NAValue::Float(fval) => {
156 if let Some(ref minval) = self.min_value {
157 let (minres, _) = Self::compare_f64(fval, minval)?;
159 return Err(OptionError::InvalidValue);
162 if let Some(ref maxval) = self.max_value {
163 let (_, maxres) = Self::compare_f64(fval, maxval)?;
165 return Err(OptionError::InvalidValue);
170 NAValue::String(ref cur_str) => {
171 if let Some(ref strings) = self.valid_strings {
172 for str in strings.iter() {
177 Err(OptionError::InvalidValue)
182 NAValue::Data(_) => Ok(()),
185 fn compare(val: i64, ref_val: &NAValue) -> OptionResult<(bool, bool)> {
187 NAValue::Int(ref_min) => {
188 Ok((val >= i64::from(*ref_min), val <= i64::from(*ref_min)))
190 NAValue::Long(ref_min) => {
191 Ok((val >= *ref_min, val <= *ref_min))
193 NAValue::Float(ref_min) => {
194 Ok(((val as f64) >= *ref_min, (val as f64) <= *ref_min))
196 _ => Err(OptionError::InvalidFormat),
199 fn compare_f64(val: f64, ref_val: &NAValue) -> OptionResult<(bool, bool)> {
201 NAValue::Int(ref_min) => {
202 Ok((val >= f64::from(*ref_min), val <= f64::from(*ref_min)))
204 NAValue::Long(ref_min) => {
205 Ok((val >= (*ref_min as f64), val <= (*ref_min as f64)))
207 NAValue::Float(ref_min) => {
208 Ok((val >= *ref_min, val <= *ref_min))
210 _ => Err(OptionError::InvalidFormat),
216 #[derive(Clone,Debug,PartialEq)]
217 pub struct NAOption {
219 pub name: &'static str,
224 /// A list of accepted option values.
225 #[derive(Debug,Clone,PartialEq)]
233 /// Long integer value.
235 /// Floating point value.
239 /// Binary data value.
243 /// Trait for all objects that handle `NAOption`.
244 pub trait NAOptionHandler {
245 /// Returns the options recognised by current object.
246 fn get_supported_options(&self) -> &[NAOptionDefinition];
247 /// Passes options for the object to set (or ignore).
248 fn set_options(&mut self, options: &[NAOption]);
249 /// Queries the current option value in the object (if present).
250 fn query_option_value(&self, name: &str) -> Option<NAValue>;
257 fn test_option_validation() {
258 let option = NAOption {name: "option", value: NAValue::Int(42) };
259 let mut def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::Float(0.0) };
260 assert!(def.check(&option).is_ok());
261 def.max_value = Some(NAValue::Long(30));
262 assert_eq!(def.check(&option), Err(OptionError::InvalidValue));
263 def.max_value = None;
264 def.min_value = Some(NAValue::Int(40));
265 assert!(def.check(&option).is_ok());
266 def.name = "option2";
267 assert_eq!(def.check(&option), Err(OptionError::WrongName));
268 let option = NAOption {name: "option", value: NAValue::String("test".to_string()) };
269 let mut def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::String("".to_string()) };
270 assert!(def.check(&option).is_ok());
271 def.valid_strings = Some(vec!["a string".to_string(), "test string".to_string()]);
272 assert_eq!(def.check(&option), Err(OptionError::InvalidValue));
273 def.valid_strings = Some(vec!["a string".to_string(), "test".to_string()]);
274 assert!(def.check(&option).is_ok());
277 fn test_option_parsing() {
278 let mut def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::Float(0.0) };
279 assert_eq!(def.parse(&"--option".to_string(), None), Err(OptionError::ParseError));
280 assert_eq!(def.parse(&"--nooption".to_string(), None), Err(OptionError::InvalidFormat));
281 assert_eq!(def.parse(&"--option".to_string(), Some(&"42".to_string())),
282 Ok((NAOption{name:"option",value: NAValue::Float(42.0)}, 2)));
283 def.max_value = Some(NAValue::Float(40.0));
284 assert_eq!(def.parse(&"--option".to_string(), Some(&"42".to_string())),
285 Err(OptionError::InvalidValue));
286 let def = NAOptionDefinition { name: "option", description: "", min_value: None, max_value: None, valid_strings: None, default_value: NAValue::Bool(true) };
287 assert_eq!(def.parse(&"option".to_string(), None),
288 Ok((NAOption{name: "option", value: NAValue::Bool(true) }, 1)));
289 assert_eq!(def.parse(&"nooption".to_string(), None),
290 Ok((NAOption{name: "option", value: NAValue::Bool(false) }, 1)));