use std::fmt;
use vgi_core::ResourceError;
pub type Result<T, E = ForgeError> = std::result::Result<T, E>;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ForgeError {
InvalidResource(ResourceError),
WrongResource {
resource: String,
expected: String,
},
NotBound {
namespace: String,
},
Unsupported {
operation: String,
hint: String,
},
NotFound {
what: String,
},
AlreadyExists {
resource: String,
forge_id: Option<u64>,
},
Moved {
what: String,
location: String,
},
Unauthorized(String),
Forbidden(String),
Rejected {
status: u16,
message: String,
},
RateLimited {
retry_after_secs: Option<u64>,
},
Unavailable(String),
BindRejected(String),
LinkFailed(String),
Webhook(String),
Config(String),
Protocol(String),
CapabilityChanged {
namespace: String,
capability: String,
available: bool,
reason: String,
},
}
impl ForgeError {
pub fn is_retryable(&self) -> bool {
matches!(
self,
ForgeError::RateLimited { .. } | ForgeError::Unavailable(_)
)
}
}
impl fmt::Display for ForgeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ForgeError::InvalidResource(e) => write!(f, "{e}"),
ForgeError::WrongResource { resource, expected } => {
write!(f, "resource `{resource}`: expected {expected}")
}
ForgeError::NotBound { namespace } => write!(
f,
"namespace `{namespace}` is not bound to this bridge; bind it before managing \
its repositories"
),
ForgeError::Unsupported { operation, hint } => {
write!(f, "{operation} is not supported here: {hint}")
}
ForgeError::NotFound { what } => write!(f, "{what}: not found (or not visible)"),
ForgeError::AlreadyExists { resource, forge_id } => match forge_id {
Some(id) => write!(f, "`{resource}` already exists (forge id {id})"),
None => write!(f, "`{resource}` already exists"),
},
ForgeError::Moved { what, location } => {
write!(
f,
"{what} has moved to {location} (renamed or transferred?)"
)
}
ForgeError::Unauthorized(m) => write!(f, "forge rejected the credentials: {m}"),
ForgeError::Forbidden(m) => write!(f, "forge refused the operation: {m}"),
ForgeError::Rejected { status, message } => {
write!(f, "forge rejected the request ({status}): {message}")
}
ForgeError::RateLimited { retry_after_secs } => match retry_after_secs {
Some(s) => write!(f, "rate-limited by the forge; retry in {s}s"),
None => write!(f, "rate-limited by the forge"),
},
ForgeError::Unavailable(m) => write!(f, "forge unavailable: {m}"),
ForgeError::BindRejected(m) => write!(f, "namespace bind rejected: {m}"),
ForgeError::LinkFailed(m) => write!(f, "account link failed: {m}"),
ForgeError::Webhook(m) => write!(f, "webhook rejected: {m}"),
ForgeError::Config(m) => write!(f, "configuration error: {m}"),
ForgeError::Protocol(m) => write!(f, "unexpected forge response: {m}"),
ForgeError::CapabilityChanged {
namespace,
capability,
available,
reason,
} => write!(
f,
"`{capability}` is now {available} for `{namespace}` ({reason}); plan again"
),
}
}
}
impl std::error::Error for ForgeError {}
impl From<ResourceError> for ForgeError {
fn from(e: ResourceError) -> Self {
ForgeError::InvalidResource(e)
}
}