use super::code::{ErrorCode, ErrorCategory};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalizedError {
pub code: ErrorCode,
pub reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<HashMap<String, String>>,
#[serde(with = "chrono::serde::ts_milliseconds")]
pub timestamp: chrono::DateTime<chrono::Utc>,
}
impl LocalizedError {
pub fn new(code: ErrorCode, reason: impl Into<String>) -> Self {
Self {
code,
reason: reason.into(),
details: None,
params: None,
timestamp: chrono::Utc::now(),
}
}
#[must_use]
pub fn with_details(mut self, details: impl Into<String>) -> Self {
self.details = Some(details.into());
self
}
#[must_use]
pub fn with_params(mut self, params: HashMap<String, String>) -> Self {
self.params = Some(params);
self
}
#[must_use]
pub fn with_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
if self.params.is_none() {
self.params = Some(HashMap::new());
}
if let Some(ref mut params) = self.params {
params.insert(key.into(), value.into());
}
self
}
#[inline]
pub fn code_value(&self) -> u32 {
self.code.as_u32()
}
#[inline]
pub fn code_str(&self) -> &'static str {
self.code.as_str()
}
#[inline]
pub fn category(&self) -> ErrorCategory {
self.code.category()
}
#[inline]
pub fn is_retryable(&self) -> bool {
self.code.is_retryable()
}
}
impl fmt::Display for LocalizedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code.as_str(), self.reason)
}
}