carbonintensity/
region.rs

1use std::{error::Error, fmt::Display, num::ParseIntError, str::FromStr};
2
3/// Region
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub enum Region {
6    NorthScotland = 1,
7    SouthScotland = 2,
8    NorthWestEngland = 3,
9    NorthEastEngland = 4,
10    SouthYorkshire = 5,
11    NorthWalesMerseysideAndCheshire = 6,
12    SouthWales = 7,
13    WestMidlands = 8,
14    EastMidlands = 9,
15    EastEngland = 10,
16    SouthWestEngland = 11,
17    SouthEngland = 12,
18    London = 13,
19    SouthEastEngland = 14,
20    England = 15,
21    Scotland = 16,
22    Wales = 17,
23}
24
25impl FromStr for Region {
26    type Err = RegionError;
27
28    fn from_str(s: &str) -> Result<Self, Self::Err> {
29        let region_id = s.parse::<u8>()?;
30
31        let region = match region_id {
32            1 => Self::NorthScotland,
33            2 => Self::SouthScotland,
34            3 => Self::NorthWestEngland,
35            4 => Self::NorthEastEngland,
36            5 => Self::SouthYorkshire,
37            6 => Self::NorthWalesMerseysideAndCheshire,
38            7 => Self::SouthWales,
39            8 => Self::WestMidlands,
40            9 => Self::EastMidlands,
41            10 => Self::EastEngland,
42            11 => Self::SouthWestEngland,
43            12 => Self::SouthEngland,
44            13 => Self::London,
45            14 => Self::SouthEastEngland,
46            15 => Self::England,
47            16 => Self::Scotland,
48            17 => Self::Wales,
49            _ => return Err(RegionError::OutsideRange),
50        };
51
52        Ok(region)
53    }
54}
55
56impl Display for Region {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        let s = match self {
59            Region::NorthScotland => "North Scotland",
60            Region::SouthScotland => "South Scotland",
61            Region::NorthWestEngland => "North West England",
62            Region::NorthEastEngland => "North East England",
63            Region::SouthYorkshire => "South Yorkshire",
64            Region::NorthWalesMerseysideAndCheshire => "North Wales, Merseyside and Cheshire",
65            Region::SouthWales => "South Wales",
66            Region::WestMidlands => "West Midlands",
67            Region::EastMidlands => "East Midlands",
68            Region::EastEngland => "East England",
69            Region::SouthWestEngland => "South West England",
70            Region::SouthEngland => "South England",
71            Region::London => "London",
72            Region::SouthEastEngland => "South East England",
73            Region::England => "England",
74            Region::Scotland => "Scotland",
75            Region::Wales => "Wales",
76        };
77        write!(f, "{}", s)
78    }
79}
80
81#[derive(Debug, PartialEq)]
82pub enum RegionError {
83    ParseError,
84    OutsideRange,
85}
86
87impl Error for RegionError {}
88
89impl Display for RegionError {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        let message = match self {
92            RegionError::ParseError => "Failed to parse region id",
93            RegionError::OutsideRange => {
94                "Region id outside allowed range. Must be between 1 and 17 (inclusive)"
95            }
96        };
97        write!(f, "{}", message)
98    }
99}
100
101/// Conversion from/into a `ParseIntError`
102///
103/// This is useful when using the error propagation operator (`?`)
104/// to avoid having to manually convert the error in the one returned.
105impl From<ParseIntError> for RegionError {
106    fn from(_: ParseIntError) -> Self {
107        RegionError::ParseError
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::{Region, RegionError};
114
115    #[test]
116    fn ids_match() {
117        assert_eq!(1_u8, Region::NorthScotland as u8);
118        assert_eq!(2_u8, Region::SouthScotland as u8);
119        assert_eq!(3_u8, Region::NorthWestEngland as u8);
120        assert_eq!(4_u8, Region::NorthEastEngland as u8);
121        assert_eq!(5_u8, Region::SouthYorkshire as u8);
122        assert_eq!(6_u8, Region::NorthWalesMerseysideAndCheshire as u8);
123        assert_eq!(7_u8, Region::SouthWales as u8);
124        assert_eq!(8_u8, Region::WestMidlands as u8);
125        assert_eq!(9_u8, Region::EastMidlands as u8);
126        assert_eq!(10_u8, Region::EastEngland as u8);
127        assert_eq!(11_u8, Region::SouthWestEngland as u8);
128        assert_eq!(12_u8, Region::SouthEngland as u8);
129        assert_eq!(13_u8, Region::London as u8);
130        assert_eq!(14_u8, Region::SouthEastEngland as u8);
131        assert_eq!(15_u8, Region::England as u8);
132        assert_eq!(16_u8, Region::Scotland as u8);
133        assert_eq!(17_u8, Region::Wales as u8);
134    }
135
136    #[test]
137    fn from_str() {
138        assert_eq!("1".parse::<Region>(), Ok(Region::NorthScotland));
139        assert_eq!("2".parse::<Region>(), Ok(Region::SouthScotland));
140        assert_eq!("3".parse::<Region>(), Ok(Region::NorthWestEngland));
141        assert_eq!("4".parse::<Region>(), Ok(Region::NorthEastEngland));
142        assert_eq!("5".parse::<Region>(), Ok(Region::SouthYorkshire));
143        assert_eq!(
144            "6".parse::<Region>(),
145            Ok(Region::NorthWalesMerseysideAndCheshire)
146        );
147        assert_eq!("7".parse::<Region>(), Ok(Region::SouthWales));
148        assert_eq!("8".parse::<Region>(), Ok(Region::WestMidlands));
149        assert_eq!("9".parse::<Region>(), Ok(Region::EastMidlands));
150        assert_eq!("10".parse::<Region>(), Ok(Region::EastEngland));
151        assert_eq!("11".parse::<Region>(), Ok(Region::SouthWestEngland));
152        assert_eq!("12".parse::<Region>(), Ok(Region::SouthEngland));
153        assert_eq!("13".parse::<Region>(), Ok(Region::London));
154        assert_eq!("14".parse::<Region>(), Ok(Region::SouthEastEngland));
155        assert_eq!("15".parse::<Region>(), Ok(Region::England));
156        assert_eq!("16".parse::<Region>(), Ok(Region::Scotland));
157        assert_eq!("17".parse::<Region>(), Ok(Region::Wales));
158    }
159
160    #[test]
161    fn region_display() {
162        assert_eq!(Region::NorthScotland.to_string(), "North Scotland");
163        assert_eq!(Region::SouthScotland.to_string(), "South Scotland");
164        assert_eq!(Region::NorthWestEngland.to_string(), "North West England");
165        assert_eq!(Region::NorthEastEngland.to_string(), "North East England");
166        assert_eq!(Region::SouthYorkshire.to_string(), "South Yorkshire");
167        assert_eq!(
168            Region::NorthWalesMerseysideAndCheshire.to_string(),
169            "North Wales, Merseyside and Cheshire"
170        );
171        assert_eq!(Region::SouthWales.to_string(), "South Wales");
172        assert_eq!(Region::WestMidlands.to_string(), "West Midlands");
173        assert_eq!(Region::EastMidlands.to_string(), "East Midlands");
174        assert_eq!(Region::EastEngland.to_string(), "East England");
175        assert_eq!(Region::SouthWestEngland.to_string(), "South West England");
176        assert_eq!(Region::SouthEngland.to_string(), "South England");
177        assert_eq!(Region::London.to_string(), "London");
178        assert_eq!(Region::SouthEastEngland.to_string(), "South East England");
179        assert_eq!(Region::England.to_string(), "England");
180        assert_eq!(Region::Scotland.to_string(), "Scotland");
181        assert_eq!(Region::Wales.to_string(), "Wales");
182    }
183
184    #[test]
185    fn error_display() {
186        assert_eq!(
187            RegionError::ParseError.to_string(),
188            "Failed to parse region id"
189        );
190        assert_eq!(
191            RegionError::OutsideRange.to_string(),
192            "Region id outside allowed range. Must be between 1 and 17 (inclusive)"
193        );
194    }
195
196    #[test]
197    fn error_parse_int_conversion() {
198        fn foo() -> Result<(), RegionError> {
199            // Propagate ParseIntError to test conversion to RegionError
200            let _: u8 = "foo".parse()?;
201
202            Ok(())
203        }
204
205        assert_eq!(foo(), Err(RegionError::ParseError));
206    }
207}