diff --git a/src/parse.rs b/src/parse.rs index 4688d51..88bda3b 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -21,12 +21,7 @@ pub fn parse(input: &str) -> Result, ParseError> { for token in tokenize(input) { match (&state, token) { (Expect::Number, Token::Number(number)) => { - let number = match parse_number(&number) { - Some(number) => number, - None => { - return Err(ParseError::NotValidNumber(number)); - } - }; + let number = parse_number(number)?; amount = Some(number); state = Expect::Unit; } @@ -37,12 +32,7 @@ pub fn parse(input: &str) -> Result, ParseError> { unreachable!("token stream can't contain two numbers in a row"); } (Expect::Unit, Token::Unit(unit)) => { - let unit = match parse_unit(&unit) { - Some(unit) => unit, - None => { - return Err(ParseError::UnknownUnit(unit)); - } - }; + let unit = parse_unit(unit)?; let quantity = NonMetricQuantity { amount: amount.take().expect("must have read a number to be in this state"), unit: unit, @@ -63,51 +53,51 @@ pub fn parse(input: &str) -> Result, ParseError> { Ok(quantities) } -fn parse_number(input: &str) -> Option { +fn parse_number(input: String) -> Result { let no_whitespace: String = input.chars().filter(|c| !c.is_whitespace()).collect(); - no_whitespace.parse().ok() + no_whitespace.parse().or_else(|_| Err(ParseError::NotValidNumber(input))) } -fn parse_unit(input: &str) -> Option { - match input { +fn parse_unit(input: String) -> Result { + match input.as_str() { // Length - "inch" => Some(NonMetric::Inch), - "inches" => Some(NonMetric::Inch), - "in" => Some(NonMetric::Inch), - "\"" => Some(NonMetric::Inch), - "″" => Some(NonMetric::Inch), + "inch" => Ok(NonMetric::Inch), + "inches" => Ok(NonMetric::Inch), + "in" => Ok(NonMetric::Inch), + "\"" => Ok(NonMetric::Inch), + "″" => Ok(NonMetric::Inch), - "foot" => Some(NonMetric::Foot), - "feet" => Some(NonMetric::Foot), - "ft" => Some(NonMetric::Foot), - "'" => Some(NonMetric::Foot), - "′" => Some(NonMetric::Foot), + "foot" => Ok(NonMetric::Foot), + "feet" => Ok(NonMetric::Foot), + "ft" => Ok(NonMetric::Foot), + "'" => Ok(NonMetric::Foot), + "′" => Ok(NonMetric::Foot), - "yard" => Some(NonMetric::Yard), - "yards" => Some(NonMetric::Yard), - "yd" => Some(NonMetric::Yard), + "yard" => Ok(NonMetric::Yard), + "yards" => Ok(NonMetric::Yard), + "yd" => Ok(NonMetric::Yard), - "mile" => Some(NonMetric::Mile), - "miles" => Some(NonMetric::Mile), - "mi" => Some(NonMetric::Mile), - "m" => Some(NonMetric::Mile), + "mile" => Ok(NonMetric::Mile), + "miles" => Ok(NonMetric::Mile), + "mi" => Ok(NonMetric::Mile), + "m" => Ok(NonMetric::Mile), // Weight - "ounce" => Some(NonMetric::Ounce), - "ounces" => Some(NonMetric::Ounce), - "oz" => Some(NonMetric::Ounce), + "ounce" => Ok(NonMetric::Ounce), + "ounces" => Ok(NonMetric::Ounce), + "oz" => Ok(NonMetric::Ounce), - "pound" => Some(NonMetric::Pound), - "pounds" => Some(NonMetric::Pound), - "lb" => Some(NonMetric::Pound), - "lbs" => Some(NonMetric::Pound), - "#" => Some(NonMetric::Pound), + "pound" => Ok(NonMetric::Pound), + "pounds" => Ok(NonMetric::Pound), + "lb" => Ok(NonMetric::Pound), + "lbs" => Ok(NonMetric::Pound), + "#" => Ok(NonMetric::Pound), - "stone" => Some(NonMetric::Stone), - "stones" => Some(NonMetric::Stone), - "st" => Some(NonMetric::Stone), + "stone" => Ok(NonMetric::Stone), + "stones" => Ok(NonMetric::Stone), + "st" => Ok(NonMetric::Stone), - _ => None, + _ => Err(ParseError::UnknownUnit(input)), } } @@ -204,56 +194,56 @@ mod test { #[test] fn numbers() { - assert_eq!(parse_number(""), None); - assert_eq!(parse_number("1"), Some(1.0)); - assert_eq!(parse_number("1.0"), Some(1.0)); - assert_eq!(parse_number("0.1"), Some(0.1)); - assert_eq!(parse_number("0.1."), None); - assert_eq!(parse_number("-10"), Some(-10.0)); - assert_eq!(parse_number("10\t00\u{1680}000"), Some(10_00_000.0)); + assert_eq!(parse_number("".to_string()), Err(ParseError::NotValidNumber("".to_string()))); + assert_eq!(parse_number("1".to_string()), Ok(1.0)); + assert_eq!(parse_number("1.0".to_string()), Ok(1.0)); + assert_eq!(parse_number("0.1".to_string()), Ok(0.1)); + assert_eq!(parse_number("0.1.".to_string()), Err(ParseError::NotValidNumber("0.1.".to_string()))); + assert_eq!(parse_number("-10".to_string()), Ok(-10.0)); + assert_eq!(parse_number("10\t00\u{1680}000".to_string()), Ok(10_00_000.0)); } #[test] fn units() { // Length - assert_eq!(parse_unit("inch"), Some(NonMetric::Inch)); - assert_eq!(parse_unit("inches"), Some(NonMetric::Inch)); - assert_eq!(parse_unit("in"), Some(NonMetric::Inch)); - assert_eq!(parse_unit("\""), Some(NonMetric::Inch)); - assert_eq!(parse_unit("″"), Some(NonMetric::Inch)); + assert_eq!(parse_unit("inch".to_string()), Ok(NonMetric::Inch)); + assert_eq!(parse_unit("inches".to_string()), Ok(NonMetric::Inch)); + assert_eq!(parse_unit("in".to_string()), Ok(NonMetric::Inch)); + assert_eq!(parse_unit("\"".to_string()), Ok(NonMetric::Inch)); + assert_eq!(parse_unit("″".to_string()), Ok(NonMetric::Inch)); - assert_eq!(parse_unit("foot"), Some(NonMetric::Foot)); - assert_eq!(parse_unit("feet"), Some(NonMetric::Foot)); - assert_eq!(parse_unit("ft"), Some(NonMetric::Foot)); - assert_eq!(parse_unit("'"), Some(NonMetric::Foot)); - assert_eq!(parse_unit("′"), Some(NonMetric::Foot)); + assert_eq!(parse_unit("foot".to_string()), Ok(NonMetric::Foot)); + assert_eq!(parse_unit("feet".to_string()), Ok(NonMetric::Foot)); + assert_eq!(parse_unit("ft".to_string()), Ok(NonMetric::Foot)); + assert_eq!(parse_unit("'".to_string()), Ok(NonMetric::Foot)); + assert_eq!(parse_unit("′".to_string()), Ok(NonMetric::Foot)); - assert_eq!(parse_unit("yard"), Some(NonMetric::Yard)); - assert_eq!(parse_unit("yards"), Some(NonMetric::Yard)); - assert_eq!(parse_unit("yd"), Some(NonMetric::Yard)); + assert_eq!(parse_unit("yard".to_string()), Ok(NonMetric::Yard)); + assert_eq!(parse_unit("yards".to_string()), Ok(NonMetric::Yard)); + assert_eq!(parse_unit("yd".to_string()), Ok(NonMetric::Yard)); - assert_eq!(parse_unit("mile"), Some(NonMetric::Mile)); - assert_eq!(parse_unit("miles"), Some(NonMetric::Mile)); - assert_eq!(parse_unit("mi"), Some(NonMetric::Mile)); - assert_eq!(parse_unit("m"), Some(NonMetric::Mile)); + assert_eq!(parse_unit("mile".to_string()), Ok(NonMetric::Mile)); + assert_eq!(parse_unit("miles".to_string()), Ok(NonMetric::Mile)); + assert_eq!(parse_unit("mi".to_string()), Ok(NonMetric::Mile)); + assert_eq!(parse_unit("m".to_string()), Ok(NonMetric::Mile)); // Weight - assert_eq!(parse_unit("ounce"), Some(NonMetric::Ounce)); - assert_eq!(parse_unit("ounces"), Some(NonMetric::Ounce)); - assert_eq!(parse_unit("oz"), Some(NonMetric::Ounce)); + assert_eq!(parse_unit("ounce".to_string()), Ok(NonMetric::Ounce)); + assert_eq!(parse_unit("ounces".to_string()), Ok(NonMetric::Ounce)); + assert_eq!(parse_unit("oz".to_string()), Ok(NonMetric::Ounce)); - assert_eq!(parse_unit("pound"), Some(NonMetric::Pound)); - assert_eq!(parse_unit("pounds"), Some(NonMetric::Pound)); - assert_eq!(parse_unit("lb"), Some(NonMetric::Pound)); - assert_eq!(parse_unit("lbs"), Some(NonMetric::Pound)); - assert_eq!(parse_unit("#"), Some(NonMetric::Pound)); + assert_eq!(parse_unit("pound".to_string()), Ok(NonMetric::Pound)); + assert_eq!(parse_unit("pounds".to_string()), Ok(NonMetric::Pound)); + assert_eq!(parse_unit("lb".to_string()), Ok(NonMetric::Pound)); + assert_eq!(parse_unit("lbs".to_string()), Ok(NonMetric::Pound)); + assert_eq!(parse_unit("#".to_string()), Ok(NonMetric::Pound)); - assert_eq!(parse_unit("stone"), Some(NonMetric::Stone)); - assert_eq!(parse_unit("stones"), Some(NonMetric::Stone)); - assert_eq!(parse_unit("st"), Some(NonMetric::Stone)); + assert_eq!(parse_unit("stone".to_string()), Ok(NonMetric::Stone)); + assert_eq!(parse_unit("stones".to_string()), Ok(NonMetric::Stone)); + assert_eq!(parse_unit("st".to_string()), Ok(NonMetric::Stone)); // Unknown unit - assert_eq!(parse_unit("hutenosa"), None); + assert_eq!(parse_unit("hutenosa".to_string()), Err(ParseError::UnknownUnit("hutenosa".to_string()))); } #[test]