use std::error::Error as StdError;
use foundation_errstacks::ErrorTrace;
use foundation_netio::shared::http::SimpleHeaders;
use super::code::Code;
use super::detail::ErrorDetail;
use super::wire::{WireError, WireErrorDetail};
#[derive(Debug)]
pub struct ConnectError {
code: Code,
message: String,
details: Vec<ErrorDetail>,
metadata: SimpleHeaders,
source: Option<Box<dyn StdError + Send + Sync>>,
wire_error: bool,
not_modified: bool,
}
macro_rules! code_constructors {
($($name:ident => $code:ident),+ $(,)?) => {
$(
#[doc = concat!("Construct a [`Code::", stringify!($code), "`] error.")]
#[must_use]
pub fn $name(message: impl Into<String>) -> Self {
Self::new(Code::$code, message)
}
)+
};
}
impl ConnectError {
#[must_use]
pub fn new(code: Code, message: impl Into<String>) -> Self {
Self {
code,
message: message.into(),
details: Vec::new(),
metadata: SimpleHeaders::new(),
source: None,
wire_error: false,
not_modified: false,
}
}
#[must_use]
pub fn with_source(code: Code, source: impl StdError + Send + Sync + 'static) -> Self {
let message = source.to_string();
Self {
code,
message,
details: Vec::new(),
metadata: SimpleHeaders::new(),
source: Some(Box::new(source)),
wire_error: false,
not_modified: false,
}
}
#[must_use]
pub fn wire(code: Code, message: impl Into<String>) -> Self {
let mut e = Self::new(code, message);
e.wire_error = true;
e
}
#[must_use]
pub fn not_modified() -> Self {
let mut e = Self::new(Code::Unknown, "not modified");
e.not_modified = true;
e
}
code_constructors! {
canceled => Canceled,
unknown => Unknown,
invalid_argument => InvalidArgument,
deadline_exceeded => DeadlineExceeded,
not_found => NotFound,
already_exists => AlreadyExists,
permission_denied => PermissionDenied,
resource_exhausted => ResourceExhausted,
failed_precondition => FailedPrecondition,
aborted => Aborted,
out_of_range => OutOfRange,
unimplemented => Unimplemented,
internal => Internal,
unavailable => Unavailable,
data_loss => DataLoss,
unauthenticated => Unauthenticated,
}
#[must_use]
pub fn code(&self) -> Code {
self.code
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub fn details(&self) -> &[ErrorDetail] {
&self.details
}
pub fn add_detail(&mut self, detail: ErrorDetail) {
self.details.push(detail);
}
#[must_use]
pub fn with_detail(mut self, detail: ErrorDetail) -> Self {
self.details.push(detail);
self
}
#[must_use]
pub fn metadata(&self) -> &SimpleHeaders {
&self.metadata
}
pub fn metadata_mut(&mut self) -> &mut SimpleHeaders {
&mut self.metadata
}
#[must_use]
pub fn is_wire_error(&self) -> bool {
self.wire_error
}
#[must_use]
pub fn is_not_modified(&self) -> bool {
self.not_modified
}
#[must_use]
pub fn to_wire(&self) -> WireError {
let message = if self.message.is_empty() {
None
} else {
Some(self.message.clone())
};
let details = if self.details.is_empty() {
None
} else {
Some(self.details.iter().map(WireErrorDetail::from_detail).collect())
};
WireError {
code: self.code.as_str().to_string(),
message,
details,
}
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(&self.to_wire())
}
}
impl core::fmt::Display for ConnectError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "[{}] {}", self.code, self.message)
}
}
impl StdError for ConnectError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source.as_ref().map(|s| s.as_ref() as &(dyn StdError + 'static))
}
}
impl From<ConnectError> for ErrorTrace<ConnectError> {
fn from(err: ConnectError) -> Self {
ErrorTrace::new(err)
}
}