Added macros to utils

This commit is contained in:
Franklin 2022-08-31 12:11:54 -04:00
parent 4c43aba220
commit 403c7d68b3
10 changed files with 140 additions and 15 deletions

View File

@ -1,15 +1,23 @@
use std::fmt::Display;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
/// This is for sending errors back from requests. //TODO: Add examples
/// This is for sending errors back from requests conveniently.
/// This struct contains an optional key just in /// This struct contains an optional key just in
/// case you want to deal with internationalization /// case you want to deal with internationalization.
/// It was left as optional just in case you don't /// It was left as optional just in case you don't
/// Have the time to yet... /// have the time to yet...
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MessageResource{ pub struct MessageResource{
pub key: Option<String>, pub key: Option<String>,
pub message: String pub message: String
} }
impl Display for MessageResource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MessageResource Key: {:?}, Message: {}", self.key, self.message)
}
}
impl MessageResource{ impl MessageResource{
pub fn new_empty() -> MessageResource{ pub fn new_empty() -> MessageResource{
@ -18,8 +26,9 @@ impl MessageResource{
pub fn new_from_str(msg: &str) -> MessageResource{ pub fn new_from_str(msg: &str) -> MessageResource{
MessageResource { key: None, message: String::from(msg) } MessageResource { key: None, message: String::from(msg) }
} }
pub fn new_from_err(errstr: String) -> MessageResource{ /// Just takes any error that implements display (Has a .to_string() method)
MessageResource { key: None, message: errstr } pub fn new_from_err<E: Display>(error: E) -> MessageResource{
MessageResource { key: None, message: error.to_string() }
} }
pub fn new(key: &str, msg: &str) -> MessageResource{ pub fn new(key: &str, msg: &str) -> MessageResource{
MessageResource { key: Some(String::from(key)), message: String::from(msg) } MessageResource { key: Some(String::from(key)), message: String::from(msg) }

37
src/enums/error.rs Normal file
View File

@ -0,0 +1,37 @@
use std::fmt::{self};
use crate::dtos::message::MessageResource;
/// This is supposed to be used whenever you have an error in your code and want to be more specific about it.
/// Fits in with most CRUD web apps. What you send back to the client is a MessageResource, not the error itself!
pub enum Error{
/// Takes a Message and the query
DatabaseError(MessageResource, String),
/// Same as UnexpectedStatusCode but without the extra details.
ClientError(MessageResource),
/// Takes the status code you expected, the actual status code, and the ErrorMessage. This is meant to be used when your app tries to use an API, be it internal or external.
UnexpectedStatusCode(u16, u16, MessageResource),
/// Try and never use this error, unless you really need to.
Unspecified,
/// If you had an error serializing/deserializing and wish to display more details. Such as the entire Json as a string, this is how.
SerdeError(MessageResource, String),
/// Normally used in compute heavy operations, such as Hashing.
ComputeError(MessageResource),
/// Self explanatory, Network related error.
NetworkError(MessageResource)
}
impl fmt::Display for Error{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *&self {
Error::Unspecified => write!(f, "Error of type Unspecified."),
Error::NetworkError(message) => write!(f, "Error of type Network.\nMessageResource: {}", message),
Error::UnexpectedStatusCode(expected, actual, message) => write!(f, "Error of type UnexpectedStatusCode.\nExpected: {}\nActual: {}\nreceivedMessageResource: {:?}", expected, actual, message),
Error::ClientError(message) => write!(f, "Error of type Client.\nMessageResource: {}", message),
Error::SerdeError(message, recieved) => write!(f, "Error of type Serialization/Deserialization.\nMessageResource: {:?}, Object attempted to be serded: {}", message, recieved),
Error::DatabaseError(message, query) => write!(f, "Error of type Database.\nMessageResource: {}, \nQuery: {}", message, query),
Error::ComputeError(message) => write!(f, "Error of type Compute.\nMessageResource: {}", message),
}
}
}

5
src/enums/macro_enums.rs Normal file
View File

@ -0,0 +1,5 @@
/// These enums just provide more modularity and readability to Macros defined in this crate.
pub enum ReturnValue {
}

2
src/enums/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod error;
pub mod macro_enums;

View File

@ -3,7 +3,7 @@ use serde::Serialize;
use crate::dtos::message::MessageResource; use crate::dtos::message::MessageResource;
/// This is what it takes [MessageResource](crate::dtos::message) this is what it returns [MessageResource](crate::dtos::message) /// Defines a type for actix web routes. As the current implementation of HttpResponse doesn't let you manually specify a type.
/// ``` /// ```
/// ///
/// use actix_web::{web::{Path}, http::StatusCode}; /// use actix_web::{web::{Path}, http::StatusCode};
@ -25,25 +25,24 @@ pub struct TypedHttpResponse<B: Serialize = String> {
impl<B: Serialize> TypedHttpResponse<B> { impl<B: Serialize> TypedHttpResponse<B> {
/// Returns a response with the json struct you define inside + Status code /// Returns a response with the json struct you define inside + Status code
/// ``` /// ```
/// use actix_web::{http::StatusCode};
/// use actix_web_utils::extensions::typed_response::TypedHttpResponse; /// use actix_web_utils::extensions::typed_response::TypedHttpResponse;
/// ///
/// TypedHttpResponse::return_standard_response(StatusCode::OK, String::from("Anything in here")); //Instead of string you can put anything here as long as it is serializable. /// TypedHttpResponse::return_standard_response(200, String::from("Anything in here")); //Instead of string you can put anything here as long as it is serializable.
/// ///
/// ``` /// ```
pub fn return_standard_response(status_code: StatusCode, body: B) -> TypedHttpResponse<B>{ pub fn return_standard_response(status_code: u16, body: B) -> TypedHttpResponse<B>{
TypedHttpResponse { TypedHttpResponse {
response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Ok(body)))) response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Ok(body))))
} }
} }
/// Returns a response with the json error list inside + Status code /// Returns a response with the json error list inside + Status code
pub fn return_standard_error_list(status_code: StatusCode, body: Vec<MessageResource>) -> TypedHttpResponse<B>{ pub fn return_standard_error_list(status_code: u16, body: Vec<MessageResource>) -> TypedHttpResponse<B>{
TypedHttpResponse { TypedHttpResponse {
response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Err(body)))) response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Err(body))))
} }
} }
/// This is to return a MessageResource wrapped inside an HttpResponse /// This is to return a MessageResource wrapped inside an HttpResponse
pub fn return_standard_error(status_code: StatusCode, body: MessageResource) -> TypedHttpResponse<B>{ pub fn return_standard_error(status_code: u16, body: MessageResource) -> TypedHttpResponse<B>{
TypedHttpResponse { TypedHttpResponse {
response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Err(vec![body])))) response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Err(vec![body]))))
} }
@ -51,7 +50,7 @@ impl<B: Serialize> TypedHttpResponse<B> {
/// Returns an empty http response with a status code /// Returns an empty http response with a status code
/// This is a bad practice, but I still left it here /// This is a bad practice, but I still left it here
/// as it is useful for debugging & specific cases /// as it is useful for debugging & specific cases
pub fn return_empty_response(status_code: StatusCode) -> TypedHttpResponse<B>{ pub fn return_empty_response(status_code: u16) -> TypedHttpResponse<B>{
TypedHttpResponse { TypedHttpResponse {
response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), None) response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), None)
} }

View File

@ -1,3 +1,5 @@
pub mod extensions; pub mod extensions;
pub mod dtos; pub mod dtos;
pub mod utils; pub mod utils;
pub mod enums;
pub mod traits;

View File

@ -0,0 +1,41 @@
use std::fmt::Display;
use log::debug;
use serde::Serialize;
use crate::{dtos::message::MessageResource, enums::error::Error, extensions::typed_response::TypedHttpResponse};
/// This trait aims to aid macros defined in this crate so that the macro can take any shape of error and
/// do the same thing for all.
pub trait ReturnableErrorShape {
fn convert_to_returnable<T: Serialize> (&self, status_code: u16) -> TypedHttpResponse<T>;
}
impl ReturnableErrorShape for MessageResource {
fn convert_to_returnable<T: Serialize>(&self, status_code: u16) -> TypedHttpResponse<T> {
TypedHttpResponse::return_standard_error(status_code, self.clone())
}
}
impl ReturnableErrorShape for Error {
fn convert_to_returnable<T: Serialize>(&self, status_code: u16) -> TypedHttpResponse<T> {
debug!("Converted error to returnable. Error: {}", self.to_string());
match self {
Error::Unspecified => TypedHttpResponse::return_standard_error(status_code, MessageResource::new_empty()),
Error::NetworkError(message) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::UnexpectedStatusCode(_, actual, message) => TypedHttpResponse::return_standard_error(*actual, message.clone()),
Error::ClientError(message) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::SerdeError(message, _) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::DatabaseError(message, _) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::ComputeError(message) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
}
}
}
impl ReturnableErrorShape for Vec<MessageResource> {
fn convert_to_returnable<T: Serialize>(&self, status_code: u16) -> TypedHttpResponse<T> {
TypedHttpResponse::return_standard_error_list(status_code, self.to_vec())
}
}
impl ReturnableErrorShape for dyn Display {
fn convert_to_returnable<T: Serialize>(&self, status_code: u16) -> TypedHttpResponse<T> {
TypedHttpResponse::return_standard_error(status_code, MessageResource::new_from_err(self))
}
}

1
src/traits/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod macro_traits;

28
src/utils/macros.rs Normal file
View File

@ -0,0 +1,28 @@
/// This is to minimize the amount of matches made in the code
/// Give it a Result<Whatever_you_want_to_return, Error> and it'll
/// Basically unwrap the result if its there and if it isn't it'll return a handled error inside a TypedHttpResponse.
/// Default status code is InternalServerError, if you want something different pass it as the first argument as a u16.
/// If you want to also return the success result, then pass a valid status code u16 as a second argument
/// Sorry for defining the error status code first, it's to maintain uniform order.
#[allow(unused_macros)]
#[macro_export]
macro_rules! unwrap_or_return_handled_error {
( $e:expr ) => {
match $e {
Ok(value) => value,
Err(error) => return actix_web_utils::traits::macro_traits::ReturnableErrorShape::convert_to_returnable(error, 500)
}
};
( $error_status_code:literal, $e:expr ) => {
match $e {
Ok(value) => value,
Err(error) => return actix_web_utils::traits::macro_traits::ReturnableErrorShape::convert_to_returnable(error, error_status_code)
}
};
( $error_status_code:literal, $success_status_code:literal, $e:expr) => {
match $e {
Ok(value) => return actix_web_utils::typed_response::TypedHttpResponse::return_standard_response($success_status_code, value),
Err(error) => return actix_web_utils::traits::macro_traits::ReturnableErrorShape::convert_to_returnable(error, error_status_code)
}
}
}

View File

@ -1 +1,2 @@
pub mod logger_util; pub mod logger_util;
pub mod macros;