use crate::tina::data::http_status::HttpStatus;
use crate::tina::data::no_data::NoData;
use crate::tina::data::return_code::{IReturnCode, IntoDynReturnCode};
use crate::tina::data::AppResult;
use crate::tina::data::{api_schema::ApiSchema, app_error::AppError};
use crate::tina::server::http::response::ResponseAttribute;
use crate::tina::server::session::Session;
use crate::tina::util::schema::SchemaExt;
use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
use std::fmt::Debug;
use std::sync::Arc;
use std::{any::type_name, borrow::Cow};
use utoipa::openapi::{
path::Parameter, request_body::RequestBody, Array, ContentBuilder, ObjectBuilder, ResponseBuilder, Responses, ResponsesBuilder, Schema,
SchemaType,
};
use utoipa::{openapi::RefOr, ToSchema};
pub struct HttpResPage<T: Serialize = NoData> {
pub code: Arc<dyn IReturnCode>,
pub msg: Option<String>,
pub total: Option<u64>,
pub rows: Vec<T>,
pub session: Session,
}
impl<T: Serialize> HttpResPage<T> {
pub fn success(session: &Session, total: Option<u64>, rows: Vec<T>) -> Self {
Self {
code: HttpStatus::Success.into_dyn_return_code(),
msg: Some(HttpStatus::Success.get_code_description().get_string(session.get_locale())),
total,
rows,
session: session.clone(),
}
}
#[cfg(feature = "rbatis")]
pub fn from_rbatis_page(session: &Session, page: ::rbatis::sql::Page<T>) -> Self {
let total = match page.search_count {
true => Some(page.total),
false => None,
};
Self::success(session, total, page.records)
}
#[cfg(feature = "rbatis")]
pub fn from_rbatis_page_result(
session: &Session,
result: Result<::rbatis::sql::Page<T>, impl std::error::Error + Send + Sync + 'static>,
) -> Self {
match result {
Ok(d) => Self::from_rbatis_page(session, d),
Err(err) => {
let err = crate::app_error_from!(err);
Self {
code: err.get_return_code(),
msg: Some(err.get_return_msg().get_string(session.get_locale())),
total: None,
rows: vec![],
session: session.clone(),
}
}
}
}
pub fn error(session: &Session, err: AppError) -> Self {
Self {
code: err.get_return_code(),
msg: Some(err.get_return_msg().get_string(session.get_locale())),
total: None,
rows: vec![],
session: session.clone(),
}
}
pub fn is_success(&self) -> bool {
self.code.get_code() == HttpStatus::Success.get_code()
}
}
impl<T: Serialize> Serialize for HttpResPage<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(3))?;
map.serialize_entry("code", &self.code.get_code())?;
map.serialize_entry("msg", &self.msg)?;
map.serialize_entry("rows", &self.rows)?;
map.serialize_entry("total", &self.total)?;
map.end()
}
}
pub trait IntoResPage<T: Serialize> {
fn into_page_result(self, session: &Session) -> AppResult<HttpResPage<T>>;
}
#[cfg(feature = "rbatis")]
impl<T, U> IntoResPage<U> for ::rbatis::sql::Page<T>
where
T: TryInto<U>,
T::Error: Into<AppError>,
U: Serialize,
{
fn into_page_result(self, session: &Session) -> AppResult<HttpResPage<U>> {
let Self {
records,
total,
pages: _,
page_no: _,
page_size: _,
search_count,
} = self;
let mut vo = Vec::with_capacity(records.len());
for item in records.into_iter() {
vo.push(item.try_into().map_err(|err| err.into())?);
}
let total = match search_count {
true => Some(total),
false => None,
};
Ok(HttpResPage::success(session, total, vo))
}
}
impl<D: Serialize + Debug> ResponseAttribute for HttpResPage<D> {
fn success(&self) -> bool {
self.is_success()
}
fn set_success(&mut self, flag: bool) {
match flag {
true => {
self.code = HttpStatus::Success.into_dyn_return_code();
}
false => {
self.code = HttpStatus::Error.into_dyn_return_code();
}
}
}
fn get_error_message(&self) -> Option<Cow<str>> {
match self.is_success() {
true => None,
false => match self.msg.as_ref() {
None => Some(Cow::Owned(self.code.to_string())),
Some(s) => Some(Cow::Borrowed(s.as_str())),
},
}
}
}
impl<'a, D: Serialize + Debug + ToSchema<'a>> ToSchema<'a> for HttpResPage<D> {
fn schema() -> (&'a str, RefOr<Schema>) {
(
type_name::<HttpResPage<D>>(),
RefOr::T(Schema::from(
ObjectBuilder::new()
.schema_type(SchemaType::Object)
.description(Some("接口返回标准对象, web接口返回的标准对象, 包括对象数据、接口调用结果、异常信息等。"))
.property("code", Schema::from(ObjectBuilder::new().schema_type(SchemaType::Integer).description(Some("返回码"))))
.property("msg", Schema::from(ObjectBuilder::new().schema_type(SchemaType::String).description(Some("返回消息"))))
.property("total", Schema::from(ObjectBuilder::new().schema_type(SchemaType::Integer).description(Some("总记录数"))))
.property("rows", Schema::Array(Array::new(D::schema().1.default_description("数据")))),
)),
)
}
}
impl<'a, D: Serialize + Debug + ToSchema<'a>> ApiSchema for HttpResPage<D> {
fn get_request_body() -> Option<RequestBody>
where
Self: Sized,
{
None
}
fn get_request_params() -> Option<Vec<Parameter>>
where
Self: Sized,
{
None
}
fn get_responses() -> Responses
where
Self: Sized,
{
let (_, schema) = Self::schema();
let description = schema.get_description();
ResponsesBuilder::new()
.response(
"200",
ResponseBuilder::new().description(description).content("application/json", ContentBuilder::new().schema(schema).build()),
)
.build()
}
}