1// Using catch-all error types like `Box<dyn Error>` isn't recommended for
2// library code where callers might want to make decisions based on the error
3// content instead of printing it out or propagating it further. Here, we define
4// a custom error type to make it possible for callers to decide what to do next
5// when our function returns an error.
6
7use std::num::ParseIntError;
8
9#[derive(PartialEq, Debug)]
10enum CreationError {
11 Negative,
12 Zero,
13}
14
15// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
16#[derive(PartialEq, Debug)]
17enum ParsePosNonzeroError {
18 Creation(CreationError),
19 ParseInt(ParseIntError),
20}
21
22impl ParsePosNonzeroError {
23 fn from_creation(err: CreationError) -> Self {
24 Self::Creation(err)
25 }
26
27 fn from_parse_int(err: ParseIntError) -> Self {
28 Self::ParseInt(err)
29 }
30}
31
32#[derive(PartialEq, Debug)]
33struct PositiveNonzeroInteger(u64);
34
35impl PositiveNonzeroInteger {
36 fn new(value: i64) -> Result<Self, CreationError> {
37 match value {
38 x if x < 0 => Err(CreationError::Negative),
39 0 => Err(CreationError::Zero),
40 x => Ok(Self(x as u64)),
41 }
42 }
43
44 fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
45 // Return an appropriate error instead of panicking when `parse()`
46 // returns an error.
47 let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
48 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
49 Self::new(x).map_err(ParsePosNonzeroError::from_creation)
50 }
51}
52
53fn main() {
54 // You can optionally experiment here.
55}
56
57#[cfg(test)]
58mod test {
59 use super::*;
60
61 #[test]
62 fn test_parse_error() {
63 assert!(matches!(
64 PositiveNonzeroInteger::parse("not a number"),
65 Err(ParsePosNonzeroError::ParseInt(_)),
66 ));
67 }
68
69 #[test]
70 fn test_negative() {
71 assert_eq!(
72 PositiveNonzeroInteger::parse("-555"),
73 Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
74 );
75 }
76
77 #[test]
78 fn test_zero() {
79 assert_eq!(
80 PositiveNonzeroInteger::parse("0"),
81 Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
82 );
83 }
84
85 #[test]
86 fn test_positive() {
87 let x = PositiveNonzeroInteger::new(42).unwrap();
88 assert_eq!(x.0, 42);
89 assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
90 }
91}