Skip to content

Commit abd6b70

Browse files
tlyuana
authored and
ana
committed
feat: add advanced_errs2
New exercise to demonstrate traits that make it easier for other code to consume our custom error types.
1 parent 882d535 commit abd6b70

File tree

2 files changed

+235
-0
lines changed

2 files changed

+235
-0
lines changed

Diff for: exercises/advanced_errors/advanced_errs2.rs

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// advanced_errs2.rs
2+
3+
// This exercise demonstrates a few traits that are useful for custom error
4+
// types to implement, especially so that other code can consume the custom
5+
// error type more usefully.
6+
7+
// Make this compile, and make the tests pass!
8+
// Execute `rustlings hint advanced_errs2` for hints.
9+
10+
// Steps:
11+
// 1. Implement a missing trait so that `main()` will compile.
12+
// 2. Complete the partial implementation of `From` for
13+
// `ParseClimateError`.
14+
// 3. Handle the missing error cases in the `FromStr` implementation for
15+
// `Climate`.
16+
// 4. Complete the partial implementation of `Display` for
17+
// `ParseClimateError`.
18+
19+
// I AM NOT DONE
20+
21+
use std::error::Error;
22+
use std::fmt::{self, Display, Formatter};
23+
use std::num::{ParseFloatError, ParseIntError};
24+
use std::str::FromStr;
25+
26+
// This is the custom error type that we will be using for the parser for
27+
// `Climate`.
28+
#[derive(Debug, PartialEq)]
29+
enum ParseClimateError {
30+
Empty,
31+
BadLen,
32+
NoCity,
33+
ParseInt(ParseIntError),
34+
ParseFloat(ParseFloatError),
35+
}
36+
37+
// This `From` implementation allows the `?` operator to work on
38+
// `ParseIntError` values.
39+
impl From<ParseIntError> for ParseClimateError {
40+
fn from(e: ParseIntError) -> Self {
41+
Self::ParseInt(e)
42+
}
43+
}
44+
45+
// This `From` implementation allows the `?` operator to work on
46+
// `ParseFloatError` values.
47+
impl From<ParseFloatError> for ParseClimateError {
48+
fn from(e: ParseFloatError) -> Self {
49+
// TODO: Complete this function
50+
}
51+
}
52+
53+
// TODO: Implement a missing trait so that `main()` below will compile. It
54+
// is not necessary to implement any methods inside the missing trait.
55+
56+
// The `Display` trait allows for other code to obtain the error formatted
57+
// as a user-visible string.
58+
impl Display for ParseClimateError {
59+
// TODO: Complete this function so that it produces the correct strings
60+
// for each error variant.
61+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
62+
// Imports the variants to make the following code more compact.
63+
use ParseClimateError::*;
64+
match self {
65+
NoCity => write!(f, "no city name"),
66+
ParseFloat(e) => write!(f, "error parsing temperature: {}", e),
67+
_ => write!(f, "unhandled error!"),
68+
}
69+
}
70+
}
71+
72+
#[derive(Debug, PartialEq)]
73+
struct Climate {
74+
city: String,
75+
year: u32,
76+
temp: f32,
77+
}
78+
79+
// Parser for `Climate`.
80+
// 1. Split the input string into 3 fields: city, year, temp.
81+
// 2. Return an error if the string is empty or has the wrong number of
82+
// fields.
83+
// 3. Return an error if the city name is empty.
84+
// 4. Parse the year as a `u32` and return an error if that fails.
85+
// 5. Parse the temp as a `f32` and return an error if that fails.
86+
// 6. Return an `Ok` value containing the completed `Climate` value.
87+
impl FromStr for Climate {
88+
type Err = ParseClimateError;
89+
// TODO: Complete this function by making it handle the missing error
90+
// cases.
91+
fn from_str(s: &str) -> Result<Self, Self::Err> {
92+
let v: Vec<_> = s.split(',').collect();
93+
let (city, year, temp) = match &v[..] {
94+
[city, year, temp] => (city.to_string(), year, temp),
95+
_ => return Err(ParseClimateError::BadLen),
96+
};
97+
let year: u32 = year.parse()?;
98+
let temp: f32 = temp.parse()?;
99+
Ok(Climate { city, year, temp })
100+
}
101+
}
102+
103+
// Don't change anything below this line (other than to enable ignored
104+
// tests).
105+
106+
fn main() -> Result<(), Box<dyn Error>> {
107+
println!("{:?}", "Hong Kong,1999,25.7".parse::<Climate>()?);
108+
println!("{:?}", "".parse::<Climate>()?);
109+
Ok(())
110+
}
111+
112+
#[cfg(test)]
113+
mod test {
114+
use super::*;
115+
#[test]
116+
fn test_empty() {
117+
let res = "".parse::<Climate>();
118+
assert_eq!(res, Err(ParseClimateError::Empty));
119+
assert_eq!(res.unwrap_err().to_string(), "empty input");
120+
}
121+
#[test]
122+
fn test_short() {
123+
let res = "Boston,1991".parse::<Climate>();
124+
assert_eq!(res, Err(ParseClimateError::BadLen));
125+
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
126+
}
127+
#[test]
128+
fn test_long() {
129+
let res = "Paris,1920,17.2,extra".parse::<Climate>();
130+
assert_eq!(res, Err(ParseClimateError::BadLen));
131+
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
132+
}
133+
#[test]
134+
fn test_no_city() {
135+
let res = ",1997,20.5".parse::<Climate>();
136+
assert_eq!(res, Err(ParseClimateError::NoCity));
137+
assert_eq!(res.unwrap_err().to_string(), "no city name");
138+
}
139+
#[test]
140+
fn test_parse_int_neg() {
141+
let res = "Barcelona,-25,22.3".parse::<Climate>();
142+
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
143+
let err = res.unwrap_err();
144+
if let ParseClimateError::ParseInt(ref inner) = err {
145+
assert_eq!(
146+
err.to_string(),
147+
format!("error parsing year: {}", inner.to_string())
148+
);
149+
} else {
150+
unreachable!();
151+
};
152+
}
153+
#[test]
154+
fn test_parse_int_bad() {
155+
let res = "Beijing,foo,15.0".parse::<Climate>();
156+
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
157+
let err = res.unwrap_err();
158+
if let ParseClimateError::ParseInt(ref inner) = err {
159+
assert_eq!(
160+
err.to_string(),
161+
format!("error parsing year: {}", inner.to_string())
162+
);
163+
} else {
164+
unreachable!();
165+
};
166+
}
167+
#[test]
168+
fn test_parse_float() {
169+
let res = "Manila,2001,bar".parse::<Climate>();
170+
assert!(matches!(res, Err(ParseClimateError::ParseFloat(_))));
171+
let err = res.unwrap_err();
172+
if let ParseClimateError::ParseFloat(ref inner) = err {
173+
assert_eq!(
174+
err.to_string(),
175+
format!("error parsing temperature: {}", inner.to_string())
176+
);
177+
} else {
178+
unreachable!();
179+
};
180+
}
181+
#[test]
182+
fn test_parse_good() {
183+
let res = "Munich,2015,23.1".parse::<Climate>();
184+
assert_eq!(
185+
res,
186+
Ok(Climate {
187+
city: "Munich".to_string(),
188+
year: 2015,
189+
temp: 23.1,
190+
})
191+
);
192+
}
193+
#[test]
194+
#[ignore]
195+
fn test_downcast() {
196+
let res = "São Paulo,-21,28.5".parse::<Climate>();
197+
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
198+
let err = res.unwrap_err();
199+
let inner: Option<&(dyn Error + 'static)> = err.source();
200+
assert!(inner.is_some());
201+
assert!(inner.unwrap().is::<ParseIntError>());
202+
}
203+
}

Diff for: info.toml

+32
Original file line numberDiff line numberDiff line change
@@ -994,3 +994,35 @@ it to the error type of the return type of the surrounding function.
994994
Hint: You will need to write another implementation of `From` that has a
995995
different input type.
996996
"""
997+
998+
[[exercises]]
999+
name = "advanced_errs2"
1000+
path = "exercises/advanced_errors/advanced_errs2.rs"
1001+
mode = "test"
1002+
hint = """
1003+
This exercise demonstrates a few traits that are useful for custom error
1004+
types to implement. These traits make it easier for other code to consume
1005+
the custom error type.
1006+
1007+
Follow the steps in the comment near the top of the file. You will have to
1008+
supply a missing trait implementation, and complete a few incomplete ones.
1009+
1010+
You may find these pages to be helpful references:
1011+
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/define_error_type.html
1012+
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
1013+
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/wrap_error.html
1014+
1015+
Hint: What trait must our error type have for `main()` to return the return
1016+
type that it returns?
1017+
1018+
Another hint: It's not necessary to implement any methods inside the missing
1019+
trait. (Some methods have default implementations that are supplied by the
1020+
trait.)
1021+
1022+
Another hint: Consult the tests to determine which error variants (and which
1023+
error message text) to produce for certain error conditions.
1024+
1025+
Challenge: There is one test that is marked `#[ignore]`. Can you supply the
1026+
missing code that will make it pass? You may want to consult the standard
1027+
library documentation for a certain trait for more hints.
1028+
"""

0 commit comments

Comments
 (0)