diff options
Diffstat (limited to 'solutions/23_conversions')
| -rw-r--r-- | solutions/23_conversions/from_str.rs | 129 |
1 files changed, 128 insertions, 1 deletions
diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs index 4e18198..301150b 100644 --- a/solutions/23_conversions/from_str.rs +++ b/solutions/23_conversions/from_str.rs @@ -1 +1,128 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This is similar to the previous `from_into` exercise. But this time, we'll +// implement `FromStr` and return errors instead of falling back to a default +// value. Additionally, upon implementing `FromStr`, you can use the `parse` +// method on strings to generate an object of the implementor type. You can read +// more about it in the documentation: +// https://doc.rust-lang.org/std/str/trait.FromStr.html + +use std::num::ParseIntError; +use std::str::FromStr; + +#[derive(Debug, PartialEq)] +struct Person { + name: String, + age: u8, +} + +// We will use this error type for the `FromStr` implementation. +#[derive(Debug, PartialEq)] +enum ParsePersonError { + // Incorrect number of fields + BadLen, + // Empty name field + NoName, + // Wrapped error from parse::<u8>() + ParseInt(ParseIntError), +} + +impl FromStr for Person { + type Err = ParsePersonError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let mut split = s.split(','); + let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else { + // ^^^^ there should be no third element + return Err(ParsePersonError::BadLen); + }; + + if name.is_empty() { + return Err(ParsePersonError::NoName); + } + + let age = age.parse().map_err(ParsePersonError::ParseInt)?; + + Ok(Self { + name: name.into(), + age, + }) + } +} + +fn main() { + let p = "Mark,20".parse::<Person>(); + println!("{p:?}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_input() { + assert_eq!("".parse::<Person>(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn good_input() { + let p = "John,32".parse::<Person>(); + assert!(p.is_ok()); + let p = p.unwrap(); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 32); + } + + #[test] + fn missing_age() { + assert!(matches!( + "John,".parse::<Person>(), + Err(ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn invalid_age() { + assert!(matches!( + "John,twenty".parse::<Person>(), + Err(ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn missing_comma_and_age() { + assert_eq!("John".parse::<Person>(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn missing_name() { + assert_eq!(",1".parse::<Person>(), Err(ParsePersonError::NoName)); + } + + #[test] + fn missing_name_and_age() { + assert!(matches!( + ",".parse::<Person>(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn missing_name_and_invalid_age() { + assert!(matches!( + ",one".parse::<Person>(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn trailing_comma() { + assert_eq!("John,32,".parse::<Person>(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn trailing_comma_and_some_string() { + assert_eq!( + "John,32,man".parse::<Person>(), + Err(ParsePersonError::BadLen), + ); + } +} |
