1use std::borrow::Cow;
2use std::io;
3use std::string::FromUtf8Error;
4
5use async_trait::async_trait;
6use palpo_core::MatrixError;
7use salvo::http::{StatusCode, StatusError};
8use salvo::oapi::{self, EndpointOutRegister, ToSchema};
9use salvo::prelude::{Depot, Request, Response, Writer};
10use thiserror::Error;
11#[derive(Error, Debug)]
15pub enum DataError {
16 #[error("public: `{0}`")]
17 Public(String),
18 #[error("internal: `{0}`")]
19 Internal(String),
20 #[error("parse int error: `{0}`")]
21 ParseIntError(#[from] std::num::ParseIntError),
22 #[error("io: `{0}`")]
23 Io(#[from] io::Error),
24 #[error("utf8: `{0}`")]
25 FromUtf8(#[from] FromUtf8Error),
26 #[error("decoding: `{0}`")]
27 Decoding(Cow<'static, str>),
28 #[error("url parse: `{0}`")]
29 UrlParse(#[from] url::ParseError),
30 #[error("serde json: `{0}`")]
31 SerdeJson(#[from] serde_json::error::Error),
32 #[error("diesel: `{0}`")]
33 Diesel(#[from] diesel::result::Error),
34 #[error("regex: `{0}`")]
35 Regex(#[from] regex::Error),
36 #[error("pool: `{0}`")]
37 Pool(#[from] crate::PoolError),
38 #[error("utf8: `{0}`")]
39 Utf8Error(#[from] std::str::Utf8Error),
40 #[error("Matrix error: `{0}`")]
41 Matrix(#[from] palpo_core::MatrixError),
42 #[error("Uiaa error: `{0}`")]
43 Uiaa(#[from] palpo_core::client::uiaa::UiaaInfo),
44 #[error("Send error: `{0}`")]
45 Send(#[from] palpo_core::sending::SendError),
46 #[error("ID parse error: `{0}`")]
47 IdParse(#[from] palpo_core::identifiers::IdParseError),
48 #[error("CanonicalJson error: `{0}`")]
49 CanonicalJson(#[from] palpo_core::serde::CanonicalJsonError),
50 #[error("MxcUriError: `{0}`")]
51 MxcUriError(#[from] palpo_core::identifiers::MxcUriError),
52 #[error("ImageError: `{0}`")]
53 ImageError(#[from] image::ImageError),
54 #[error("Signatures: `{0}`")]
55 Signatures(#[from] palpo_core::signatures::Error),
56}
57
58impl DataError {
59 pub fn public<S: Into<String>>(msg: S) -> Self {
60 Self::Public(msg.into())
61 }
62
63 pub fn internal<S: Into<String>>(msg: S) -> Self {
64 Self::Internal(msg.into())
65 }
66}
67
68#[async_trait]
69impl Writer for DataError {
70 async fn write(mut self, req: &mut Request, depot: &mut Depot, res: &mut Response) {
71 let matrix = match self {
72 Self::Public(msg) => MatrixError::unknown(msg),
73 Self::Internal(_msg) => MatrixError::unknown("unknown error."),
74 Self::Matrix(e) => e,
75 Self::Uiaa(uiaa) => {
76 use crate::core::client::uiaa::ErrorKind;
77 if res.status_code.map(|c| c.is_success()).unwrap_or(true) {
78 let code = if let Some(error) = &uiaa.auth_error {
79 match &error.kind {
80 ErrorKind::Forbidden | ErrorKind::UserDeactivated => StatusCode::FORBIDDEN,
81 ErrorKind::NotFound => StatusCode::NOT_FOUND,
82 ErrorKind::BadState | ErrorKind::BadJson | ErrorKind::BadStatus | ErrorKind::BadAlias => {
83 StatusCode::BAD_REQUEST
84 }
85 ErrorKind::Unauthorized => StatusCode::UNAUTHORIZED,
86 ErrorKind::CannotOverwriteMedia => StatusCode::CONFLICT,
87 ErrorKind::NotYetUploaded => StatusCode::GATEWAY_TIMEOUT,
88 _ => StatusCode::INTERNAL_SERVER_ERROR,
89 }
90 } else {
91 StatusCode::UNAUTHORIZED
92 };
93 res.status_code(code);
94 }
95 res.add_header(salvo::http::header::CONTENT_TYPE, "application/json", true)
96 .ok();
97 let body: Vec<u8> = crate::core::serde::json_to_buf(&uiaa).unwrap();
98 res.write_body(body).ok();
99 return;
100 }
101 Self::Diesel(e) => {
102 tracing::error!(error = ?e, "diesel db error.");
103 if let diesel::result::Error::NotFound = e {
104 MatrixError::not_found("Resource not found.")
105 } else {
106 MatrixError::unknown("unknown db error.")
107 }
108 }
109 _ => MatrixError::unknown("unknown error happened."),
110 };
111 matrix.write(req, depot, res).await;
112 }
113}
114impl EndpointOutRegister for DataError {
115 fn register(components: &mut oapi::Components, operation: &mut oapi::Operation) {
116 operation.responses.insert(
117 StatusCode::INTERNAL_SERVER_ERROR.as_str(),
118 oapi::Response::new("Internal server error")
119 .add_content("application/json", StatusError::to_schema(components)),
120 );
121 operation.responses.insert(
122 StatusCode::NOT_FOUND.as_str(),
123 oapi::Response::new("Not found").add_content("application/json", StatusError::to_schema(components)),
124 );
125 operation.responses.insert(
126 StatusCode::BAD_REQUEST.as_str(),
127 oapi::Response::new("Bad request").add_content("application/json", StatusError::to_schema(components)),
128 );
129 }
130}