Last active
March 27, 2022 15:23
-
-
Save Doaxan/27fb41be384c349bcd8ac265853990dd to your computer and use it in GitHub Desktop.
rustlings/exercises/conversions/from_str.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // from_str.rs | |
| // This is similar to from_into.rs, 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 at https://doc.rust-lang.org/std/str/trait.FromStr.html | |
| use std::num::ParseIntError; | |
| use std::ops::Not; | |
| use std::str::FromStr; | |
| #[derive(Debug, PartialEq)] | |
| struct Person { | |
| name: String, | |
| age: usize, | |
| } | |
| // We will use this error type for the `FromStr` implementation. | |
| #[derive(Debug, PartialEq)] | |
| enum ParsePersonError { | |
| // Empty input string | |
| Empty, | |
| // Incorrect number of fields | |
| BadLen, | |
| // Empty name field | |
| NoName, | |
| // Wrapped error from parse::<usize>() | |
| ParseInt(ParseIntError), | |
| } | |
| // Steps: | |
| // 1. If the length of the provided string is 0, an error should be returned | |
| // 2. Split the given string on the commas present in it | |
| // 3. Only 2 elements should be returned from the split, otherwise return an error | |
| // 4. Extract the first element from the split operation and use it as the name | |
| // 5. Extract the other element from the split operation and parse it into a `usize` as the age | |
| // with something like `"4".parse::<usize>()` | |
| // 6. If while extracting the name and the age something goes wrong, an error should be returned | |
| // If everything goes well, then return a Result of a Person object | |
| impl FromStr for Person { | |
| type Err = ParsePersonError; | |
| fn from_str(s: &str) -> Result<Person, Self::Err> { | |
| if s.is_empty() { | |
| return Err(ParsePersonError::Empty); | |
| } | |
| let mut split = s.split(','); | |
| // Wrong, nth discard previous values | |
| // if split.nth(2).is_some() { | |
| // return Err(ParsePersonError::BadLen); | |
| // } | |
| // let (name, age) = ( | |
| // split.next().ok_or(ParsePersonError::BadLen)?, | |
| // split.next().ok_or(ParsePersonError::BadLen)?, | |
| // ); | |
| // let (name, age, _) = ( | |
| // split.next().ok_or(ParsePersonError::BadLen)?, | |
| // split.next().ok_or(ParsePersonError::BadLen)?, | |
| // split | |
| // .next() | |
| // .map_or(Ok(()), |_| Err(ParsePersonError::BadLen))?, | |
| // ); | |
| let (name, age) = match (split.next(), split.next(), split.next()) { | |
| (Some(name), Some(age), None) => ( | |
| name.is_empty() | |
| .not() | |
| .then(|| name.into()) | |
| .ok_or(ParsePersonError::NoName)?, | |
| age.parse::<usize>().map_err(ParsePersonError::ParseInt)?, | |
| ), | |
| _ => return Err(ParsePersonError::BadLen), | |
| }; | |
| Ok(Person { name, age }) | |
| } | |
| } | |
| fn main() { | |
| let p = "Mark,20".parse::<Person>().unwrap(); | |
| println!("{:?}", p); | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[test] | |
| fn empty_input() { | |
| assert_eq!("".parse::<Person>(), Err(ParsePersonError::Empty)); | |
| } | |
| #[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) | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment