|
|
use super::aiserver::v1::ErrorDetails; |
|
|
use crate::common::model::{ApiStatus, ErrorResponse as CommonErrorResponse}; |
|
|
use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _}; |
|
|
use prost::Message as _; |
|
|
use reqwest::StatusCode; |
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
|
|
#[derive(Deserialize)] |
|
|
pub struct ChatError { |
|
|
error: ErrorBody, |
|
|
} |
|
|
|
|
|
#[derive(Deserialize)] |
|
|
pub struct ErrorBody { |
|
|
code: String, |
|
|
|
|
|
details: Vec<ErrorDetail>, |
|
|
} |
|
|
|
|
|
#[derive(Deserialize)] |
|
|
pub struct ErrorDetail { |
|
|
|
|
|
|
|
|
|
|
|
value: String, |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl ChatError { |
|
|
pub fn to_error_response(self) -> ErrorResponse { |
|
|
if self.error.details.is_empty() { |
|
|
return ErrorResponse { |
|
|
status: 500, |
|
|
code: "unknown".to_string(), |
|
|
error: None, |
|
|
}; |
|
|
} |
|
|
|
|
|
let error_details = self.error.details.first().and_then(|detail| { |
|
|
STANDARD_NO_PAD |
|
|
.decode(&detail.value) |
|
|
.ok() |
|
|
.map(bytes::Bytes::from) |
|
|
.and_then(|buf| ErrorDetails::decode(buf).ok()) |
|
|
}); |
|
|
|
|
|
let status = error_details |
|
|
.as_ref() |
|
|
.map(|details| details.status_code()) |
|
|
.unwrap_or(500); |
|
|
|
|
|
ErrorResponse { |
|
|
status, |
|
|
code: self.error.code, |
|
|
error: error_details |
|
|
.and_then(|details| details.details) |
|
|
.map(|custom_details| Error { |
|
|
message: custom_details.title, |
|
|
details: custom_details.detail, |
|
|
}), |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
#[derive(Serialize)] |
|
|
pub struct ErrorResponse { |
|
|
pub status: u16, |
|
|
pub code: String, |
|
|
#[serde(skip_serializing_if = "Option::is_none")] |
|
|
pub error: Option<Error>, |
|
|
} |
|
|
|
|
|
#[derive(Serialize)] |
|
|
pub struct Error { |
|
|
pub message: String, |
|
|
pub details: String, |
|
|
|
|
|
} |
|
|
|
|
|
impl ErrorResponse { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn status_code(&self) -> StatusCode { |
|
|
StatusCode::from_u16(self.status).unwrap() |
|
|
} |
|
|
|
|
|
pub fn native_code(&self) -> String { |
|
|
self.error.as_ref().map_or_else( |
|
|
|| self.code.replace("_", " "), |
|
|
|error| error.message.clone(), |
|
|
) |
|
|
} |
|
|
|
|
|
pub fn to_common(self) -> CommonErrorResponse { |
|
|
CommonErrorResponse { |
|
|
status: ApiStatus::Error, |
|
|
code: Some(self.status), |
|
|
error: self |
|
|
.error |
|
|
.as_ref() |
|
|
.map(|error| error.message.clone()) |
|
|
.or(Some(self.code.clone())), |
|
|
message: self.error.as_ref().map(|error| error.details.clone()), |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
pub enum StreamError { |
|
|
ChatError(ChatError), |
|
|
DataLengthLessThan5, |
|
|
EmptyStream, |
|
|
} |
|
|
|
|
|
impl std::fmt::Display for StreamError { |
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
|
match self { |
|
|
StreamError::ChatError(error) => write!(f, "{}", error.error.code), |
|
|
StreamError::DataLengthLessThan5 => write!(f, "data length less than 5"), |
|
|
StreamError::EmptyStream => write!(f, "empty stream"), |
|
|
} |
|
|
} |
|
|
} |
|
|
|