use std::fmt;
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorCode {
Unauthenticated,
Forbidden,
BadRequest,
NotFound,
MethodNotAllowed,
BodyTooLarge,
Internal,
StorageError,
AlreadyEnrolled,
TooLarge,
TooManyUploads,
BadChunkSize,
BadChunkIndex,
BadChunkLength,
ChunkTooLarge,
ChunkDigestMismatch,
ChunkConflict,
AlreadyComplete,
Finalizing,
HashMismatch,
Incomplete,
RevisionConflict,
PreconditionRequired,
IdempotencyKeyReuse,
DuplicateDelivery,
RateLimited,
OauthUnavailable,
Other(String),
}
impl ErrorCode {
pub fn from_code(code: &str) -> Self {
match code {
"unauthenticated" => Self::Unauthenticated,
"forbidden" => Self::Forbidden,
"bad_request" => Self::BadRequest,
"not_found" => Self::NotFound,
"method_not_allowed" => Self::MethodNotAllowed,
"body_too_large" => Self::BodyTooLarge,
"internal" => Self::Internal,
"storage_error" => Self::StorageError,
"already_enrolled" => Self::AlreadyEnrolled,
"too_large" => Self::TooLarge,
"too_many_uploads" => Self::TooManyUploads,
"bad_chunk_size" => Self::BadChunkSize,
"bad_chunk_index" => Self::BadChunkIndex,
"bad_chunk_length" => Self::BadChunkLength,
"chunk_too_large" => Self::ChunkTooLarge,
"chunk_digest_mismatch" => Self::ChunkDigestMismatch,
"chunk_conflict" => Self::ChunkConflict,
"already_complete" => Self::AlreadyComplete,
"finalizing" => Self::Finalizing,
"hash_mismatch" => Self::HashMismatch,
"incomplete" => Self::Incomplete,
"revision_conflict" => Self::RevisionConflict,
"precondition_required" => Self::PreconditionRequired,
"idempotency_key_reuse" => Self::IdempotencyKeyReuse,
"duplicate_delivery" => Self::DuplicateDelivery,
"rate_limited" => Self::RateLimited,
"oauth_unavailable" => Self::OauthUnavailable,
other => Self::Other(other.to_string()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Refusal {
pub status: u16,
pub code: ErrorCode,
pub body: Value,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Refused(Refusal),
Tls(String),
Io(std::io::Error),
Protocol(String),
NoRoots,
Config(String),
Key(String),
Source(String),
BadReply(String),
}
impl Error {
pub fn code(&self) -> Option<&ErrorCode> {
match self {
Error::Refused(r) => Some(&r.code),
_ => None,
}
}
pub fn status(&self) -> Option<u16> {
match self {
Error::Refused(r) => Some(r.status),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Refused(r) => write!(f, "daemon refused with {} ({:?})", r.status, r.code),
Error::Tls(e) => write!(f, "tls: {e}"),
Error::Io(e) => write!(f, "io: {e}"),
Error::Protocol(e) => write!(f, "protocol: {e}"),
Error::NoRoots => f.write_str("no root certificate configured"),
Error::Config(e) => write!(f, "config: {e}"),
Error::Key(e) => write!(f, "key: {e}"),
Error::Source(e) => write!(f, "upload source: {e}"),
Error::BadReply(e) => write!(f, "unreadable reply: {e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}