use std::error as stderr;
use std::ffi::CStr;
use std::fmt;
use uplink_sys as ulksys;
pub(crate) type BoxError = Box<dyn stderr::Error + Send + Sync>;
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
Internal(Internal),
InvalidArguments(Args),
Uplink(Uplink),
}
impl Error {
pub(crate) fn new_internal(ctx_msg: &str, err: BoxError) -> Self {
Error::Internal(Internal {
ctx_msg: String::from(ctx_msg),
inner: err,
})
}
pub(crate) fn new_invalid_arguments(names: &str, msg: &str) -> Self {
Self::InvalidArguments(Args::new(names, msg))
}
pub(crate) fn new_uplink(err: *mut ulksys::UplinkError) -> Option<Self> {
Uplink::new(err).map(Self::Uplink)
}
pub(crate) fn from_ffi_error(err: *mut ulksys::UplinkError) -> Option<Self> {
Uplink::from_ffi_error(err).map(Self::Uplink)
}
}
impl stderr::Error for Error {
fn source(&self) -> Option<&(dyn stderr::Error + 'static)> {
match self {
Error::InvalidArguments { .. } => None,
Error::Uplink { .. } => None,
Error::Internal(Internal { inner, .. }) => Some(inner.as_ref()),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Error::InvalidArguments(args) => {
write!(f, "{}", args)
}
Error::Uplink(details) => {
write!(f, "{}", details)
}
Error::Internal(details) => {
write!(f, "{}", details)
}
}
}
}
#[derive(Debug)]
pub struct Args {
pub names: String,
pub msg: String,
}
impl Args {
fn new(names: &str, msg: &str) -> Self {
Args {
names: String::from(names),
msg: String::from(msg),
}
}
}
impl fmt::Display for Args {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
f,
"{} arguments have invalid values. {}",
self.names, self.msg
)
}
}
#[derive(Debug)]
pub enum Uplink {
Internal(String),
Canceled(String),
InvalidHandle(String),
TooManyRequests(String),
BandwidthLimitExceeded(String),
BucketNameInvalid(String),
BucketAlreadyExists(String),
BucketNotEmpty(String),
BucketNotFound(String),
ObjectKeyInvalid(String),
ObjectNotFound(String),
PermissionDenied(String),
SegmentsLimitExceeded(String),
StorageLimitExceeded(String),
UploadDone(String),
EdgeAuthDialFailed(String),
EdgeRegisterAccessFailed(String),
Unknown(String),
}
impl Uplink {
pub(crate) fn new(err: *mut ulksys::UplinkError) -> Option<Self> {
if err.is_null() {
return None;
}
let ulkerr = unsafe { *err };
let msg = if ulkerr.message.is_null() {
String::new()
} else {
unsafe {
CStr::from_ptr(ulkerr.message)
.to_str()
.expect("invalid FFI error message; it contains non UTF-8 characters")
.to_string()
}
};
Some(match ulkerr.code as u32 {
ulksys::UPLINK_ERROR_INTERNAL => Self::Internal(msg),
ulksys::UPLINK_ERROR_CANCELED => Self::Canceled(msg),
ulksys::UPLINK_ERROR_INVALID_HANDLE => Self::InvalidHandle(msg),
ulksys::UPLINK_ERROR_TOO_MANY_REQUESTS => Self::TooManyRequests(msg),
ulksys::UPLINK_ERROR_BANDWIDTH_LIMIT_EXCEEDED => Self::BandwidthLimitExceeded(msg),
ulksys::UPLINK_ERROR_BUCKET_NAME_INVALID => Self::BucketNameInvalid(msg),
ulksys::UPLINK_ERROR_BUCKET_ALREADY_EXISTS => Self::BucketAlreadyExists(msg),
ulksys::UPLINK_ERROR_BUCKET_NOT_EMPTY => Self::BucketNotEmpty(msg),
ulksys::UPLINK_ERROR_BUCKET_NOT_FOUND => Self::BucketNotFound(msg),
ulksys::UPLINK_ERROR_OBJECT_KEY_INVALID => Self::ObjectKeyInvalid(msg),
ulksys::UPLINK_ERROR_OBJECT_NOT_FOUND => Self::ObjectNotFound(msg),
ulksys::UPLINK_ERROR_PERMISSION_DENIED => Self::PermissionDenied(msg),
ulksys::UPLINK_ERROR_SEGMENTS_LIMIT_EXCEEDED => Self::SegmentsLimitExceeded(msg),
ulksys::UPLINK_ERROR_STORAGE_LIMIT_EXCEEDED => Self::StorageLimitExceeded(msg),
ulksys::UPLINK_ERROR_UPLOAD_DONE => Self::UploadDone(msg),
ulksys::EDGE_ERROR_AUTH_DIAL_FAILED => Self::EdgeAuthDialFailed(msg),
ulksys::EDGE_ERROR_REGISTER_ACCESS_FAILED => Self::EdgeRegisterAccessFailed(msg),
_ => Self::Unknown(msg),
})
}
pub(crate) fn from_ffi_error(err: *mut ulksys::UplinkError) -> Option<Self> {
let opt = Self::new(err);
if opt.is_some() {
unsafe { ulksys::uplink_free_error(err) };
}
opt
}
}
impl fmt::Display for Uplink {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let (code, details) = match self {
Self::Internal(msg) => ("internal", msg),
Self::Canceled(msg) => ("canceled", msg),
Self::InvalidHandle(msg) => ("invalid handle", msg),
Self::TooManyRequests(msg) => ("too many requests", msg),
Self::BandwidthLimitExceeded(msg) => ("bandwidth limit exceeded", msg),
Self::BucketNameInvalid(msg) => ("bucket name invalid", msg),
Self::BucketAlreadyExists(msg) => ("bucket already exists", msg),
Self::BucketNotEmpty(msg) => ("bucket not empty", msg),
Self::BucketNotFound(msg) => ("bucket not found", msg),
Self::ObjectKeyInvalid(msg) => ("object key invalid", msg),
Self::ObjectNotFound(msg) => ("object not found", msg),
Self::PermissionDenied(msg) => ("permission denied", msg),
Self::SegmentsLimitExceeded(msg) => ("segments limit exceeded", msg),
Self::StorageLimitExceeded(msg) => ("storage limit exceeded", msg),
Self::UploadDone(msg) => ("upload done", msg),
Self::EdgeAuthDialFailed(msg) => ("dial to auth service failed", msg),
Self::EdgeRegisterAccessFailed(msg) => ("register access for edge service failed", msg),
Self::Unknown(msg) => ("unknown", msg),
};
write!(f, r#"code: "{}", details: "{}""#, code, details)
}
}
#[derive(Debug)]
pub struct Internal {
pub ctx_msg: String,
inner: BoxError,
}
impl fmt::Display for Internal {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self.ctx_msg)
}
}
impl stderr::Error for Internal {
fn source(&self) -> Option<&(dyn stderr::Error + 'static)> {
Some(self.inner.as_ref())
}
}