use std::fmt::Debug;
use bytes::BufMut;
use http::{HeaderMap, HeaderValue};
use http_body::Body;
use prost::Message;
use crate::{
app_error_from,
tina::{
constant::Constants,
data::{app_error::AppError, grpc::response_data::GrpcResData},
grpc::{FromGrpcResponse, IntoGrpcResponse},
},
};
pub type BoxBody = http_body::combinators::UnsyncBoxBody<bytes::Bytes, AppError>;
pub struct Response {
pub(crate) inner: tonic::Response<BoxBody>,
}
impl Response {
pub fn new(body: BoxBody) -> Self {
Self {
inner: tonic::Response::new(body),
}
}
pub fn from_parts<T: Message + Debug>(metadata: tonic::metadata::MetadataMap, data: T, extensions: tonic::Extensions) -> Self {
let encoded_len = data.encoded_len();
let mut buf = bytes::BytesMut::with_capacity(std::mem::size_of::<u8>() + std::mem::size_of::<u32>() + encoded_len);
buf.put_u8(0);
buf.put_u32(encoded_len as u32);
data.encode(&mut buf).unwrap_or_else(|err| panic!("Encode buf failed, reason: {err:?}, data: {data:?}"));
let body = buf.freeze();
let body = http_body::Full::new(body).map_err(app_error_from!()).boxed_unsync();
Self {
inner: tonic::Response::from_parts(metadata, body, extensions),
}
}
}
impl<T: Message + Debug, E: Message + Debug> From<tonic::Response<Result<T, E>>> for Response {
fn from(value: tonic::Response<Result<T, E>>) -> Self {
let (metadata, r, extensions) = value.into_parts();
match r {
Ok(data) => Self::from_parts(metadata, data, extensions),
Err(err) => Self::from_parts(metadata, err, extensions),
}
}
}
impl Debug for Response {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
#[async_trait]
impl IntoGrpcResponse for AppError {
type Response = tonic::Response<AppError>;
async fn into_grpc_response(self) -> Self::Response {
let mut res_headers = HeaderMap::new();
res_headers.insert(Constants::RESPONSE_ERROR_HEADER, HeaderValue::from_static("true"));
res_headers.insert("content-type", HeaderValue::from_static("application/grpc"));
let status = tonic::Status::from(&self);
if let Err(err) = status.add_header(&mut res_headers) {
tracing::error!("Add gRPC header failed: {err:?}");
}
let metadata = tonic::metadata::MetadataMap::from_headers(res_headers);
tonic::Response::from_parts(metadata, self, tonic::Extensions::default())
}
}
#[async_trait]
impl FromGrpcResponse for AppError {
type Response = tonic::Response<AppError>;
type Rejection = AppError;
async fn from_grpc_response(res: Self::Response) -> Result<Self, Self::Rejection>
where
Self: Sized,
{
Ok(res.into_inner())
}
}
#[async_trait]
impl<D> IntoGrpcResponse for GrpcResData<D>
where
D: Message + Debug + Send + Sync + 'static,
{
type Response = tonic::Response<D>;
async fn into_grpc_response(self) -> Self::Response {
let mut res_headers = self.metadata;
res_headers.insert(Constants::RESPONSE_ERROR_HEADER, HeaderValue::from_static("false"));
res_headers.insert("content-type", HeaderValue::from_static("application/grpc"));
let status = tonic::Status::new(tonic::Code::Ok, "");
if let Err(err) = status.add_header(&mut res_headers) {
tracing::error!("Add gRPC header failed: {err:?}");
}
let metadata = tonic::metadata::MetadataMap::from_headers(res_headers);
tonic::Response::from_parts(metadata, self.data, tonic::Extensions::default())
}
}
#[async_trait]
impl<D> FromGrpcResponse for GrpcResData<D>
where
D: Message + Default + Debug + Send + Sync + 'static,
{
type Response = tonic::Response<D>;
type Rejection = AppError;
async fn from_grpc_response(res: Self::Response) -> Result<Self, Self::Rejection>
where
Self: Sized,
{
let (metadata, message, extensions) = res.into_parts();
let mut metadata = metadata.into_headers();
let mut extensions = extensions.into_http();
let mut data = GrpcResData::new(message);
{
std::mem::swap(&mut data.metadata, &mut metadata);
}
{
std::mem::swap(&mut data.extensions, &mut extensions);
}
Ok(data)
}
}
#[async_trait]
impl<D, E, DM, EM> IntoGrpcResponse for Result<D, E>
where
D: IntoGrpcResponse<Response = tonic::Response<DM>> + Debug + Send + Sync + 'static,
E: IntoGrpcResponse<Response = tonic::Response<EM>> + Debug + Send + Sync + 'static,
DM: Message + Debug + Send + Sync + 'static,
EM: Message + Debug + Send + Sync + 'static,
{
type Response = tonic::Response<Result<DM, EM>>;
async fn into_grpc_response(self) -> Self::Response {
match self {
Ok(data) => {
let res = data.into_grpc_response().await;
let (metadata, message, extensions) = res.into_parts();
tonic::Response::from_parts(metadata, Ok(message), extensions)
}
Err(err) => {
let res = err.into_grpc_response().await;
let (metadata, message, extensions) = res.into_parts();
tonic::Response::from_parts(metadata, Err(message), extensions)
}
}
}
}
#[async_trait]
impl<D, E, DM, EM> FromGrpcResponse for Result<D, E>
where
D: FromGrpcResponse<Response = tonic::Response<DM>, Rejection = AppError> + Debug + Send + Sync + 'static,
E: FromGrpcResponse<Response = tonic::Response<EM>, Rejection = AppError> + Debug + Send + Sync + 'static,
DM: Message + Debug + Send + Sync + 'static,
EM: Message + Debug + Send + Sync + 'static,
{
type Response = tonic::Response<Result<DM, EM>>;
type Rejection = E::Rejection;
async fn from_grpc_response(res: Self::Response) -> Result<Self, Self::Rejection>
where
Self: Sized,
{
tracing::trace!("Result receive response: {:?}", res);
let (metadata, r, extensions) = res.into_parts();
match r {
Ok(v) => {
let res = tonic::Response::from_parts(metadata, v, extensions);
match D::from_grpc_response(res).await {
Ok(v) => Ok(Ok(v)),
Err(err) => Err(err),
}
}
Err(err) => {
let res = tonic::Response::from_parts(metadata, err, extensions);
match E::from_grpc_response(res).await {
Ok(v) => Ok(Err(v)),
Err(err) => Err(err),
}
}
}
}
}