use std::fmt::{Debug, Display};
use displaydoc::Display;
use jiff::civil::{Date, Time};
pub type Result<T, Endpoint> = std::result::Result<T, ClientError<Endpoint>>;
#[derive(Debug, Display)]
#[ignore_extra_doc_attributes]
pub enum ClientError<Endpoint: Debug + Display> {
InvalidSystemId,
InvalidApiKey,
DisabledApiKey,
ReadOnlyKey,
#[displaydoc("Bad API key.")]
BadApiKey,
RateLimitExceeded,
DonationModeRequired,
InaccessibleSystemId,
NotFound,
#[displaydoc("An unrecognised unsuccessful HTTP status code of {0} was returned with this message: ({1:?})")]
UnrecognisedStatusCode(reqwest::StatusCode, Option<String>),
#[displaydoc("An error occurred while making the request: {0}")]
Reqwest(reqwest::Error),
#[displaydoc("Deserialisation error: {0}")]
Parse(csv::Error),
#[displaydoc("Error writing CSV data to a buffer: {0}")]
Write(Box<csv::IntoInnerError<csv::Writer<Vec<u8>>>>),
#[displaydoc("Failed to parse rate limit information from the response's HTTP headers: {0}")]
RateLimitParse(RateLimitError),
MissingField,
#[displaydoc("An endpoint-specific error occurred: {0}")]
EndpointSpecific(Endpoint),
}
impl<Endpoint> From<reqwest::Error> for ClientError<Endpoint>
where
Endpoint: Debug + Display,
{
fn from(error: reqwest::Error) -> Self { Self::Reqwest(error) }
}
impl<Endpoint> From<csv::Error> for ClientError<Endpoint>
where
Endpoint: Debug + Display,
{
fn from(error: csv::Error) -> Self { Self::Parse(error) }
}
impl<Endpoint> From<RateLimitError> for ClientError<Endpoint>
where
Endpoint: Debug + Display,
{
fn from(error: RateLimitError) -> Self { Self::RateLimitParse(error) }
}
impl<Endpoint> From<csv::IntoInnerError<csv::Writer<Vec<u8>>>> for ClientError<Endpoint>
where
Endpoint: Debug + Display,
{
fn from(error: csv::IntoInnerError<csv::Writer<Vec<u8>>>) -> Self { Self::Write(Box::new(error)) }
}
impl From<AddOutputError> for ClientError<AddOutputError> {
fn from(error: AddOutputError) -> Self { Self::EndpointSpecific(error) }
}
impl From<AddStatusError> for ClientError<AddStatusError> {
fn from(error: AddStatusError) -> Self { Self::EndpointSpecific(error) }
}
impl From<PostSystemError> for ClientError<PostSystemError> {
fn from(error: PostSystemError) -> Self { Self::EndpointSpecific(error) }
}
impl<Endpoint> std::error::Error for ClientError<Endpoint> where Endpoint: Debug + Display {}
#[derive(Debug, Display, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum NoEndpointSpecificError {}
#[derive(Debug, Display, PartialEq, Eq, Clone)]
pub enum DateTimeError {
#[displaydoc("An invalid date was provided: {0}")]
InvalidDate(String),
#[displaydoc("An invalid time was provided: {0}")]
InvalidTime(String),
#[displaydoc("The date \"{0}\" is too old.")]
DateTooOld(Date),
#[displaydoc("The date \"{0}\" is too new.")]
DateTooNew(Date),
MissingDateOrTime,
}
impl std::error::Error for DateTimeError {}
#[derive(Debug, Display, PartialEq, Clone)]
pub enum AddOutputError {
NoData,
InvalidDateTime(DateTimeError),
#[displaydoc("The generation value {generation} is too high for the system size {system_size} on {date}.")]
GenerationTooHigh {
generation: f32,
system_size: u32,
date: Date,
},
#[displaydoc("The export value {export} is too high for the system size {system_size} on {date}.")]
ExportTooHigh { export: f32, system_size: u32, date: Date },
#[displaydoc("The export value {export} exceeds the generation value {generation} by more than 15%.")]
ExportExceedsGeneration { export: f32, generation: f32 },
#[displaydoc("The consumption value {0} is above 999,999,999 Wh on {1}.")]
ConsumptionTooHigh(f32, Date),
#[displaydoc("The peak power {peak_power} is ≥50% greater than the system size {system_size} on {date}.")]
PeakPowerTooHigh {
peak_power: f32,
system_size: u32,
date: Date,
},
#[displaydoc("A maximum temperature was specified on {0} without a minimum temperature.")]
MaxTemperatureWithoutMin(Date),
#[displaydoc("A minimum temperature was specified on {0} without a maximum temperature.")]
MinTemperatureWithoutMax(Date),
}
impl std::error::Error for AddOutputError {}
#[derive(Debug, Display, PartialEq, Eq, Clone)]
pub enum AddStatusError {
NoData,
InvalidDateTime(DateTimeError),
NoEnergyData,
MoonPowered,
BothCumulativeAndNet,
#[displaydoc("The energy generation value {0} is too high for the time period {1}.")]
EnergyTooHighForTime(u32, Time),
#[displaydoc("The power generation value {power} is too high for this system with size {system_size}.")]
ExcessivePowerGeneration { power: u32, system_size: u32 },
#[displaydoc("The energy generation value {energy} is too high for this system with size {system_size}.")]
ExcessiveEnergyGeneration { energy: u32, system_size: u32 },
MissingFields,
#[displaydoc("Invalid data format: {0}.")]
InvalidDataFormat(String),
}
impl std::error::Error for AddStatusError {}
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq)]
#[ignore_extra_doc_attributes]
pub enum PostSystemError {
SystemNameTooLong,
#[displaydoc("Missing label for extended data variable v{0}.")]
MissingExtendedDataLabel(u8),
#[displaydoc("Missing unit for extended data variable v{0}.")]
MissingExtendedDataUnit(u8),
#[displaydoc("Label too long for extended data variable v{0}.")]
ExtendedDataLabelTooLong(u8),
#[displaydoc("Unit too long for extended data variable v{0}.")]
ExtendedDataUnitTooLong(u8),
#[displaydoc("Field {1} is invalid for extended data variable v{0}.")]
InvalidExtendedDataField(u8, ExtendedDataField),
}
impl std::error::Error for PostSystemError {}
#[derive(Debug, Display, PartialEq, Eq, Clone, Copy, Hash)]
pub enum ExtendedDataField {
Colour,
Axis,
Graph,
}
#[derive(Debug, Display)]
pub enum RateLimitError {
MissingValues,
NonAsciiHeader,
#[displaydoc("Failed to parse integer header value: {0}")]
InvalidInteger(std::num::ParseIntError),
#[displaydoc("Failed to parse the `reset` header value: {0}")]
InvalidTimestamp(jiff::Error),
}
impl std::error::Error for RateLimitError {}
impl From<http::header::ToStrError> for RateLimitError {
fn from(_: http::header::ToStrError) -> Self { Self::NonAsciiHeader }
}
impl From<std::num::ParseIntError> for RateLimitError {
fn from(error: std::num::ParseIntError) -> Self { Self::InvalidInteger(error) }
}
impl From<jiff::Error> for RateLimitError {
fn from(error: jiff::Error) -> Self { Self::InvalidTimestamp(error) }
}