tina-core 0.0.2

Tina platform
Documentation
//! Response扩展
use crate::tina::data::app_error::AppError;
use crate::tina::data::http::response_data::HttpResData;
use crate::tina::data::http::response_page::HttpResPage;
use crate::tina::data::http::response_stream::{AjaxStreamInner, BinaryContentStreamAdapter, ResStream};
use crate::tina::data::http::{ErrorMsg, ErrorResponse, JsonResponse, StreamResponse, SuccessFlag};
use crate::tina::data::json::ToJson;
use crate::tina::data::no_data::NoData;
use crate::tina::data::AppResult;
use crate::tina::server::http::response::{HttpResponseExt, ResponseAttribute};
use crate::tina::server::session::Session;
use crate::tina::util::json::JsonUtil;
use axum::body::{boxed, BoxBody, StreamBody};
use axum::response::IntoResponse;
use http::header::{HeaderName, CONTENT_DISPOSITION, CONTENT_LENGTH, CONTENT_TYPE, LAST_MODIFIED};
use http::response::Builder;
use http::{HeaderValue, StatusCode};
use http_body::Empty;
use serde::{Serialize, Serializer};
use std::borrow::Cow;
use std::fmt::Debug;

/// 响应
pub struct ResRaw {
    inner: axum::response::Response,
}

/// 响应
pub type Response<T = BoxBody> = axum::response::Response<T>;

impl<T> ResponseAttribute for Response<T> {
    fn success(&self) -> bool {
        let ext = self.extensions();
        ext.get::<SuccessFlag>().is_some()
    }

    fn set_success(&mut self, flag: bool) {
        let ext = self.extensions_mut();
        match flag {
            true => {
                ext.insert(SuccessFlag);
            }
            false => {
                ext.remove::<SuccessFlag>();
            }
        }
    }

    fn get_error_message(&self) -> Option<Cow<str>> {
        let ext = self.extensions();
        match ext.get::<SuccessFlag>() {
            None => ext.get::<ErrorMsg>().map(|s| Cow::Owned(s.0.to_string())),
            Some(_) => None,
        }
    }
}

impl ResponseAttribute for ResRaw {
    fn success(&self) -> bool {
        self.inner.success()
    }

    fn set_success(&mut self, flag: bool) {
        self.inner.set_success(flag)
    }

    fn get_error_message(&self) -> Option<Cow<str>> {
        self.inner.get_error_message()
    }
}

impl ResRaw {
    /// 构建
    pub fn new(res: axum::response::Response) -> Self {
        Self {
            inner: res,
        }
    }
    /// 转换
    pub fn into_inner(self) -> axum::response::Response {
        self.inner
    }
}

/// 转换成ResRaw
pub trait IntoResRaw {
    /// 转换
    fn into_ajax_response(self) -> ResRaw;
}

impl IntoResRaw for Response {
    fn into_ajax_response(self) -> ResRaw {
        ResRaw::new(self)
    }
}

impl Serialize for ResRaw {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str("[Response]")
    }
}

impl IntoResponse for ResRaw {
    #[inline]
    fn into_response(mut self) -> axum::response::Response {
        self.inner.set_success(self.success());
        self.inner
    }
}

/// 转换成Response
pub trait IntoResponse2 {
    /// 转换
    fn into_response2(self, locale: &str, print_err_log: bool) -> Response;
}

impl<D, Ext> IntoResponse2 for HttpResData<D, Ext>
where
    D: Serialize + Debug,
    Ext: Serialize + Debug,
{
    fn into_response2(self, _locale: &str, print_err_log: bool) -> Response {
        let body = JsonUtil::to_json_string(&self);

        if print_err_log {
            match self.is_success() {
                true => {}
                false => {
                    tracing::error!("{}", body);
                }
            }
        }
        Builder::new().status(StatusCode::OK).header(CONTENT_TYPE, "application/json").body(boxed(body)).expect("build response failed")
    }
}

impl<D> IntoResponse2 for HttpResPage<D>
where
    D: Serialize + Debug,
{
    fn into_response2(self, _locale: &str, print_err_log: bool) -> Response {
        let body = JsonUtil::to_json_string(&self);

        if print_err_log {
            match self.is_success() {
                true => {}
                false => {
                    tracing::error!("{}", body);
                }
            }
        }
        Builder::new().status(StatusCode::OK).header(CONTENT_TYPE, "application/json").body(boxed(body)).expect("build response failed")
    }
}

impl IntoResponse2 for ResStream {
    fn into_response2(self, locale: &str, print_err_log: bool) -> Response {
        match self.inner {
            AjaxStreamInner::Success(stream_data) => {
                let data = stream_data.data;
                let content_type = stream_data.content_type;
                let name = stream_data.name;
                let download = stream_data.download;
                let mut builder = Builder::new().status(StatusCode::OK).header(CONTENT_TYPE, content_type.as_ref());
                if download {
                    builder = builder.header(CONTENT_DISPOSITION, format!("attachment;filename={}", name));
                }
                if let Some(last_modified) = stream_data.last_modified.as_ref() {
                    builder = builder.header(LAST_MODIFIED, last_modified.to_string());
                }
                for (k, v) in self.headers.into_iter() {
                    if let Some(k) = k {
                        builder = builder.header(k, v);
                    }
                }
                let mut res = match stream_data.size {
                    None => {
                        let body = StreamBody::new(BinaryContentStreamAdapter(data));
                        builder.body(boxed(body)).expect("build stream response body failed...")
                    }
                    Some(size) => {
                        builder = builder.header(CONTENT_LENGTH, size);
                        let body = StreamBody::new(BinaryContentStreamAdapter(data));
                        builder.body(boxed(body)).expect("build stream response body failed...")
                    }
                };
                res.set_success(true);
                res
            }
            AjaxStreamInner::AppError(err) => {
                let mut res = err.into_response2(locale, print_err_log);
                res.set_success(false);
                res
            }
            AjaxStreamInner::HttpStatus(status_code) => {
                Builder::new().status(status_code).body(boxed(Empty::default())).expect("build stream response body failed...")
            }
        }
    }
}

impl IntoResponse2 for JsonResponse {
    fn into_response2(self, _locale: &str, _print_err_log: bool) -> Response {
        let body = self.0.to_json_string();
        Builder::new().status(StatusCode::OK).header(CONTENT_TYPE, "application/json").body(boxed(body)).expect("build response failed")
    }
}

impl IntoResponse2 for AppError {
    fn into_response2(self, locale: &str, print_err_log: bool) -> Response {
        let msg = format!("{:?}", self);
        let vo: HttpResData<NoData> = HttpResData {
            code: self.get_return_code(),
            msg: Some(self.get_return_msg().get_string(locale)),
            data: None,
            extension: Default::default(),
            session: Session::default(),
        };

        let body = vo.to_json_string();
        if print_err_log {
            match vo.is_success() {
                true => {}
                false => {
                    tracing::error!("{}, 异常信息: {}", body, msg);
                }
            }
        }
        Builder::new().status(StatusCode::OK).header(CONTENT_TYPE, "application/json").body(boxed(body)).expect("build response failed")
    }
}

impl IntoResponse2 for ErrorResponse {
    fn into_response2(self, locale: &str, print_err_log: bool) -> Response {
        self.0.into_response2(locale, print_err_log)
    }
}

impl IntoResponse2 for StreamResponse {
    fn into_response2(self, locale: &str, print_err_log: bool) -> Response {
        self.0.into_response2(locale, print_err_log)
    }
}

impl<T> HttpResponseExt for Response<T> {
    fn add_response_header(&mut self, header_name: HeaderName, header_value: HeaderValue) -> AppResult<()> {
        let headers = self.headers_mut();
        headers.insert(header_name, header_value);
        Ok(())
    }
}