use crate::Error;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProviderOutcome {
pub provider: String,
pub status: ProviderStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ProviderStatus {
Success,
Unavailable,
RateLimited,
Failed,
}
impl ProviderOutcome {
pub fn success(provider: impl Into<String>) -> Self {
Self {
provider: provider.into(),
status: ProviderStatus::Success,
message: None,
}
}
pub fn from_error(provider: impl Into<String>, error: &Error) -> Self {
let status = match error {
Error::Reqwest(error)
if error.status() == Some(reqwest::StatusCode::TOO_MANY_REQUESTS) =>
{
ProviderStatus::RateLimited
}
Error::Reqwest(error) if error.is_timeout() || error.is_connect() => {
ProviderStatus::Unavailable
}
_ => ProviderStatus::Failed,
};
Self {
provider: provider.into(),
status,
message: Some(error.to_string()),
}
}
pub fn succeeded(&self) -> bool {
self.status == ProviderStatus::Success
}
pub fn aggregate(provider: impl Into<String>, outcomes: &[Self]) -> Self {
let status = outcomes
.iter()
.map(|outcome| outcome.status)
.max_by_key(|status| match status {
ProviderStatus::Success => 0,
ProviderStatus::Unavailable => 1,
ProviderStatus::RateLimited => 2,
ProviderStatus::Failed => 3,
})
.unwrap_or(ProviderStatus::Success);
let messages: Vec<_> = outcomes
.iter()
.filter_map(|outcome| outcome.message.as_deref())
.collect();
Self {
provider: provider.into(),
status,
message: (!messages.is_empty()).then(|| messages.join("; ")),
}
}
}
pub fn default_success() -> ProviderOutcome {
ProviderOutcome::success("legacy")
}
impl std::fmt::Display for ProviderStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Success => "success",
Self::Unavailable => "unavailable",
Self::RateLimited => "rate_limited",
Self::Failed => "failed",
})
}
}