1use std::fmt::{Debug, Display};
4
5use displaydoc::Display;
6use jiff::civil::{Date, Time};
7
8pub type Result<T, Endpoint> = std::result::Result<T, ClientError<Endpoint>>;
9
10#[derive(Debug, Display)]
21#[ignore_extra_doc_attributes]
22pub enum ClientError<Endpoint: Debug + Display> {
23 InvalidSystemId,
25
26 InvalidApiKey,
28
29 DisabledApiKey,
31
32 ReadOnlyKey,
35
36 #[displaydoc("Bad API key.")]
39 BadApiKey,
40
41 RateLimitExceeded,
46
47 DonationModeRequired,
49
50 InaccessibleSystemId,
53
54 NotFound,
56
57 #[displaydoc("An unrecognised unsuccessful HTTP status code of {0} was returned with this message: ({1:?})")]
60 UnrecognisedStatusCode(reqwest::StatusCode, Option<String>),
61
62 #[displaydoc("An error occurred while making the request: {0}")]
64 Reqwest(reqwest::Error),
65
66 #[displaydoc("Deserialisation error: {0}")]
68 Parse(csv::Error),
69
70 #[displaydoc("Error writing CSV data to a buffer: {0}")]
72 Write(Box<csv::IntoInnerError<csv::Writer<Vec<u8>>>>),
73
74 #[displaydoc("Failed to parse rate limit information from the response's HTTP headers: {0}")]
77 RateLimitParse(RateLimitError),
78
79 MissingField,
81
82 #[displaydoc("An endpoint-specific error occurred: {0}")]
84 EndpointSpecific(Endpoint),
85}
86
87impl<Endpoint> From<reqwest::Error> for ClientError<Endpoint>
88where
89 Endpoint: Debug + Display,
90{
91 fn from(error: reqwest::Error) -> Self { Self::Reqwest(error) }
92}
93
94impl<Endpoint> From<csv::Error> for ClientError<Endpoint>
95where
96 Endpoint: Debug + Display,
97{
98 fn from(error: csv::Error) -> Self { Self::Parse(error) }
99}
100
101impl<Endpoint> From<RateLimitError> for ClientError<Endpoint>
102where
103 Endpoint: Debug + Display,
104{
105 fn from(error: RateLimitError) -> Self { Self::RateLimitParse(error) }
106}
107
108impl<Endpoint> From<csv::IntoInnerError<csv::Writer<Vec<u8>>>> for ClientError<Endpoint>
109where
110 Endpoint: Debug + Display,
111{
112 fn from(error: csv::IntoInnerError<csv::Writer<Vec<u8>>>) -> Self { Self::Write(Box::new(error)) }
113}
114
115impl From<AddOutputError> for ClientError<AddOutputError> {
116 fn from(error: AddOutputError) -> Self { Self::EndpointSpecific(error) }
117}
118
119impl From<AddStatusError> for ClientError<AddStatusError> {
120 fn from(error: AddStatusError) -> Self { Self::EndpointSpecific(error) }
121}
122
123impl From<PostSystemError> for ClientError<PostSystemError> {
124 fn from(error: PostSystemError) -> Self { Self::EndpointSpecific(error) }
125}
126
127impl<Endpoint> std::error::Error for ClientError<Endpoint> where Endpoint: Debug + Display {}
128
129#[derive(Debug, Display, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
131pub enum NoEndpointSpecificError {}
132
133#[derive(Debug, Display, PartialEq, Eq, Clone)]
135pub enum DateTimeError {
136 #[displaydoc("An invalid date was provided: {0}")]
138 InvalidDate(String),
139
140 #[displaydoc("An invalid time was provided: {0}")]
142 InvalidTime(String),
143
144 #[displaydoc("The date \"{0}\" is too old.")]
146 DateTooOld(Date),
147
148 #[displaydoc("The date \"{0}\" is too new.")]
150 DateTooNew(Date),
151
152 MissingDateOrTime,
154}
155
156impl std::error::Error for DateTimeError {}
157
158#[derive(Debug, Display, PartialEq, Clone)]
160pub enum AddOutputError {
161 NoData,
163
164 InvalidDateTime(DateTimeError),
166
167 #[displaydoc("The generation value {generation} is too high for the system size {system_size} on {date}.")]
169 GenerationTooHigh {
170 generation: f32,
171 system_size: u32,
172 date: Date,
173 },
174
175 #[displaydoc("The export value {export} is too high for the system size {system_size} on {date}.")]
177 ExportTooHigh { export: f32, system_size: u32, date: Date },
178
179 #[displaydoc("The export value {export} exceeds the generation value {generation} by more than 15%.")]
181 ExportExceedsGeneration { export: f32, generation: f32 },
182
183 #[displaydoc("The consumption value {0} is above 999,999,999 Wh on {1}.")]
185 ConsumptionTooHigh(f32, Date),
186
187 #[displaydoc("The peak power {peak_power} is ≥50% greater than the system size {system_size} on {date}.")]
189 PeakPowerTooHigh {
190 peak_power: f32,
191 system_size: u32,
192 date: Date,
193 },
194
195 #[displaydoc("A maximum temperature was specified on {0} without a minimum temperature.")]
197 MaxTemperatureWithoutMin(Date),
198
199 #[displaydoc("A minimum temperature was specified on {0} without a maximum temperature.")]
201 MinTemperatureWithoutMax(Date),
202}
203
204impl std::error::Error for AddOutputError {}
205
206#[derive(Debug, Display, PartialEq, Eq, Clone)]
209pub enum AddStatusError {
210 NoData,
212
213 InvalidDateTime(DateTimeError),
215
216 NoEnergyData,
218
219 MoonPowered,
221
222 BothCumulativeAndNet,
224
225 #[displaydoc("The energy generation value {0} is too high for the time period {1}.")]
227 EnergyTooHighForTime(u32, Time),
228
229 #[displaydoc("The power generation value {power} is too high for this system with size {system_size}.")]
231 ExcessivePowerGeneration { power: u32, system_size: u32 },
232
233 #[displaydoc("The energy generation value {energy} is too high for this system with size {system_size}.")]
235 ExcessiveEnergyGeneration { energy: u32, system_size: u32 },
236
237 MissingFields,
239
240 #[displaydoc("Invalid data format: {0}.")]
242 InvalidDataFormat(String),
243}
244
245impl std::error::Error for AddStatusError {}
246
247#[derive(Debug, Display, Copy, Clone, PartialEq, Eq)]
249#[ignore_extra_doc_attributes]
250pub enum PostSystemError {
251 SystemNameTooLong,
255
256 #[displaydoc("Missing label for extended data variable v{0}.")]
258 MissingExtendedDataLabel(u8),
259
260 #[displaydoc("Missing unit for extended data variable v{0}.")]
262 MissingExtendedDataUnit(u8),
263
264 #[displaydoc("Label too long for extended data variable v{0}.")]
268 ExtendedDataLabelTooLong(u8),
269
270 #[displaydoc("Unit too long for extended data variable v{0}.")]
274 ExtendedDataUnitTooLong(u8),
275
276 #[displaydoc("Field {1} is invalid for extended data variable v{0}.")]
278 InvalidExtendedDataField(u8, ExtendedDataField),
279}
280
281impl std::error::Error for PostSystemError {}
282
283#[derive(Debug, Display, PartialEq, Eq, Clone, Copy, Hash)]
286pub enum ExtendedDataField {
287 Colour,
289
290 Axis,
292
293 Graph,
295}
296
297#[derive(Debug, Display)]
300pub enum RateLimitError {
301 MissingValues,
303
304 NonAsciiHeader,
306
307 #[displaydoc("Failed to parse integer header value: {0}")]
309 InvalidInteger(std::num::ParseIntError),
310
311 #[displaydoc("Failed to parse the `reset` header value: {0}")]
313 InvalidTimestamp(jiff::Error),
314}
315
316impl std::error::Error for RateLimitError {}
317
318impl From<http::header::ToStrError> for RateLimitError {
319 fn from(_: http::header::ToStrError) -> Self { Self::NonAsciiHeader }
320}
321
322impl From<std::num::ParseIntError> for RateLimitError {
323 fn from(error: std::num::ParseIntError) -> Self { Self::InvalidInteger(error) }
324}
325
326impl From<jiff::Error> for RateLimitError {
327 fn from(error: jiff::Error) -> Self { Self::InvalidTimestamp(error) }
328}