use crate::error::ErrorKind;
use http::Method;
use serde::ser::SerializeStruct;
use serde::Serialize;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct ErrorContext {
last_dispatched_from: Option<String>,
last_dispatched_to: Option<String>,
extended_context: Option<ExtendedErrorContext>,
}
impl ErrorContext {
pub(crate) fn new( ) -> Self {
Self {
last_dispatched_from: None,
last_dispatched_to: None,
extended_context: None,
}
}
pub(crate) fn with_dispatched_from(mut self, dispatched_from: String) -> Self {
self.last_dispatched_from = Some(dispatched_from);
self
}
pub(crate) fn with_dispatched_to(mut self, dispatched_to: String) -> Self {
self.last_dispatched_to = Some(dispatched_to);
self
}
pub(crate) fn with_extended_context(mut self, context: ExtendedErrorContext) -> Self {
self.extended_context = Some(context);
self
}
pub fn last_dispatched_to(&self) -> Option<&String> {
self.last_dispatched_to.as_ref()
}
pub fn last_dispatched_from(&self) -> Option<&String> {
self.last_dispatched_from.as_ref()
}
}
impl Serialize for ErrorContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("context", 0)?;
if let Some(ref last_dispatched_from) = self.last_dispatched_from {
state.serialize_field("last_dispatched_from", last_dispatched_from)?;
}
if let Some(ref last_dispatched_to) = self.last_dispatched_to {
state.serialize_field("last_dispatched_to", last_dispatched_to)?;
}
if let Some(ref extended_context) = self.extended_context {
state.serialize_field("extended_context", extended_context)?;
}
state.end()
}
}
impl Display for ErrorContext {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", json!(&self))
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum ExtendedErrorContext {
KeyValue(KeyValueErrorContext),
Query(QueryErrorContext),
Search(SearchErrorContext),
Http(HttpErrorContext),
}
impl Serialize for ExtendedErrorContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
ExtendedErrorContext::KeyValue(context) => context.serialize(serializer),
ExtendedErrorContext::Query(context) => context.serialize(serializer),
ExtendedErrorContext::Search(context) => context.serialize(serializer),
ExtendedErrorContext::Http(context) => context.serialize(serializer),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub(crate) struct KeyValueErrorContext {
pub(crate) key: String,
pub(crate) opaque: u32,
pub(crate) op_code: u8,
pub(crate) status: u16,
pub(crate) bucket_name: String,
pub(crate) scope_name: String,
pub(crate) collection_name: String,
pub(crate) error_name: Option<String>,
pub(crate) error_desc: Option<String>,
pub(crate) xcontent: Option<String>,
pub(crate) xref: Option<String>,
pub(crate) source_message: Option<String>,
}
impl KeyValueErrorContext {
pub fn new(
key: String,
opaque: u32,
op_code: u8,
status: u16,
bucket_name: String,
scope_name: String,
collection_name: String,
) -> Self {
Self {
key,
opaque,
op_code,
status,
bucket_name,
scope_name,
collection_name,
error_name: None,
error_desc: None,
xcontent: None,
xref: None,
source_message: None,
}
}
pub fn with_error_name(mut self, error_name: String) -> Self {
self.error_name = Some(error_name);
self
}
pub fn with_error_desc(mut self, error_desc: String) -> Self {
self.error_desc = Some(error_desc);
self
}
pub fn with_xcontent(mut self, xcontent: String) -> Self {
self.xcontent = Some(xcontent);
self
}
pub fn with_xref(mut self, xref: String) -> Self {
self.xref = Some(xref);
self
}
pub fn with_source_message(mut self, source_message: String) -> Self {
self.source_message = Some(source_message);
self
}
}
impl Serialize for KeyValueErrorContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("context", 7)?;
state.serialize_field("key", &self.key)?;
state.serialize_field("opaque", &self.opaque)?;
state.serialize_field("opCode", &self.op_code)?;
state.serialize_field("status", &self.status)?;
state.serialize_field("bucketName", &self.bucket_name)?;
state.serialize_field("scopeName", &self.scope_name)?;
state.serialize_field("collectionName", &self.collection_name)?;
if let Some(ref error_name) = self.error_name {
state.serialize_field("errorName", error_name)?;
}
if let Some(ref error_desc) = self.error_desc {
state.serialize_field("errorDesc", error_desc)?;
}
if let Some(ref xcontent) = self.xcontent {
state.serialize_field("xcontent", xcontent)?;
}
if let Some(ref xref) = self.xref {
state.serialize_field("xref", xref)?;
}
if let Some(ref source_message) = self.source_message {
state.serialize_field("sourceMessage", source_message)?;
}
state.end()
}
}
impl Display for KeyValueErrorContext {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", json!(&self))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct QueryErrorDesc {
pub(crate) kind: ErrorKind,
pub(crate) code: u32,
pub(crate) message: String,
pub(crate) retry: bool,
pub(crate) reason: HashMap<String, Value>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct QueryErrorContext {
pub(crate) statement: String,
pub(crate) code: Option<u32>,
pub(crate) message: Option<String>,
pub(crate) client_context_id: String,
pub(crate) http_status_code: Option<http::StatusCode>,
pub(crate) descs: Vec<QueryErrorDesc>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct SearchErrorContext {
pub(crate) index_name: String,
pub(crate) error_text: Option<String>,
pub(crate) http_status_code: Option<http::StatusCode>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct HttpErrorContext {
pub(crate) status_code: http::StatusCode,
pub(crate) path: String,
pub(crate) method: Method,
pub(crate) error_text: Option<String>,
}
impl Serialize for QueryErrorDesc {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("context", 4)?;
state.serialize_field("code", &self.code)?;
state.serialize_field("message", &self.message)?;
state.serialize_field("retry", &self.retry)?;
state.serialize_field("reason", &self.reason)?;
state.end()
}
}
impl Serialize for QueryErrorContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("context", 2)?;
state.serialize_field("statement", &self.statement)?;
state.serialize_field("clientContextId", &self.client_context_id)?;
if let Some(ref code) = self.code {
state.serialize_field("code", &code)?;
}
if let Some(ref message) = self.message {
state.serialize_field("message", message)?;
}
if let Some(ref http_status_code) = self.http_status_code {
state.serialize_field("httpStatusCode", &http_status_code.as_u16())?;
}
state.serialize_field("descs", &self.descs)?;
state.end()
}
}
impl Display for QueryErrorContext {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", json!(&self))
}
}
impl Serialize for SearchErrorContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("context", 1)?;
state.serialize_field("indexName", &self.index_name)?;
if let Some(ref error_text) = self.error_text {
state.serialize_field("errorText", error_text)?;
}
if let Some(ref http_status_code) = self.http_status_code {
state.serialize_field("httpStatusCode", &http_status_code.as_u16())?;
}
state.end()
}
}
impl Serialize for HttpErrorContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("context", 3)?;
state.serialize_field("statusCode", &self.status_code.as_u16())?;
state.serialize_field("path", &self.path)?;
state.serialize_field("method", &self.method.to_string())?;
if let Some(ref error_text) = self.error_text {
state.serialize_field("errorText", error_text)?;
}
state.end()
}
}