From 403c7d68b359779757797016832acaaec0349e74 Mon Sep 17 00:00:00 2001 From: Franklin Date: Wed, 31 Aug 2022 12:11:54 -0400 Subject: [PATCH] Added macros to utils --- src/dtos/message.rs | 21 +++++++++++----- src/enums/error.rs | 37 ++++++++++++++++++++++++++++ src/enums/macro_enums.rs | 5 ++++ src/enums/mod.rs | 2 ++ src/extensions/typed_response.rs | 13 +++++----- src/lib.rs | 4 +++- src/traits/macro_traits.rs | 41 ++++++++++++++++++++++++++++++++ src/traits/mod.rs | 1 + src/utils/macros.rs | 28 ++++++++++++++++++++++ src/utils/mod.rs | 3 ++- 10 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 src/enums/error.rs create mode 100644 src/enums/macro_enums.rs create mode 100644 src/enums/mod.rs create mode 100644 src/traits/macro_traits.rs create mode 100644 src/traits/mod.rs create mode 100644 src/utils/macros.rs diff --git a/src/dtos/message.rs b/src/dtos/message.rs index 29ba731..b3fcfd2 100644 --- a/src/dtos/message.rs +++ b/src/dtos/message.rs @@ -1,15 +1,23 @@ +use std::fmt::Display; + 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 -/// 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 -/// Have the time to yet... -#[derive(Serialize, Deserialize, Debug)] +/// have the time to yet... +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct MessageResource{ pub key: Option, 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{ pub fn new_empty() -> MessageResource{ @@ -18,8 +26,9 @@ impl MessageResource{ pub fn new_from_str(msg: &str) -> MessageResource{ MessageResource { key: None, message: String::from(msg) } } - pub fn new_from_err(errstr: String) -> MessageResource{ - MessageResource { key: None, message: errstr } + /// Just takes any error that implements display (Has a .to_string() method) + pub fn new_from_err(error: E) -> MessageResource{ + MessageResource { key: None, message: error.to_string() } } pub fn new(key: &str, msg: &str) -> MessageResource{ MessageResource { key: Some(String::from(key)), message: String::from(msg) } diff --git a/src/enums/error.rs b/src/enums/error.rs new file mode 100644 index 0000000..f70f9dd --- /dev/null +++ b/src/enums/error.rs @@ -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), + } + } +} \ No newline at end of file diff --git a/src/enums/macro_enums.rs b/src/enums/macro_enums.rs new file mode 100644 index 0000000..2b373a3 --- /dev/null +++ b/src/enums/macro_enums.rs @@ -0,0 +1,5 @@ + +/// These enums just provide more modularity and readability to Macros defined in this crate. +pub enum ReturnValue { + +} \ No newline at end of file diff --git a/src/enums/mod.rs b/src/enums/mod.rs new file mode 100644 index 0000000..16cc5d5 --- /dev/null +++ b/src/enums/mod.rs @@ -0,0 +1,2 @@ +pub mod error; +pub mod macro_enums; \ No newline at end of file diff --git a/src/extensions/typed_response.rs b/src/extensions/typed_response.rs index b2e23d8..ba5e7b0 100644 --- a/src/extensions/typed_response.rs +++ b/src/extensions/typed_response.rs @@ -3,7 +3,7 @@ use serde::Serialize; 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}; @@ -25,25 +25,24 @@ pub struct TypedHttpResponse { impl TypedHttpResponse { /// 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; /// - /// 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{ + pub fn return_standard_response(status_code: u16, body: B) -> TypedHttpResponse{ TypedHttpResponse { 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 - pub fn return_standard_error_list(status_code: StatusCode, body: Vec) -> TypedHttpResponse{ + pub fn return_standard_error_list(status_code: u16, body: Vec) -> TypedHttpResponse{ TypedHttpResponse { 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 - pub fn return_standard_error(status_code: StatusCode, body: MessageResource) -> TypedHttpResponse{ + pub fn return_standard_error(status_code: u16, body: MessageResource) -> TypedHttpResponse{ TypedHttpResponse { response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Err(vec![body])))) } @@ -51,7 +50,7 @@ impl TypedHttpResponse { /// Returns an empty http response with a status code /// This is a bad practice, but I still left it here /// as it is useful for debugging & specific cases - pub fn return_empty_response(status_code: StatusCode) -> TypedHttpResponse{ + pub fn return_empty_response(status_code: u16) -> TypedHttpResponse{ TypedHttpResponse { response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), None) } diff --git a/src/lib.rs b/src/lib.rs index d32b9d0..0f17331 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ pub mod extensions; pub mod dtos; -pub mod utils; \ No newline at end of file +pub mod utils; +pub mod enums; +pub mod traits; \ No newline at end of file diff --git a/src/traits/macro_traits.rs b/src/traits/macro_traits.rs new file mode 100644 index 0000000..28d4029 --- /dev/null +++ b/src/traits/macro_traits.rs @@ -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 (&self, status_code: u16) -> TypedHttpResponse; +} +impl ReturnableErrorShape for MessageResource { + fn convert_to_returnable(&self, status_code: u16) -> TypedHttpResponse { + TypedHttpResponse::return_standard_error(status_code, self.clone()) + } +} +impl ReturnableErrorShape for Error { + fn convert_to_returnable(&self, status_code: u16) -> TypedHttpResponse { + 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 { + fn convert_to_returnable(&self, status_code: u16) -> TypedHttpResponse { + TypedHttpResponse::return_standard_error_list(status_code, self.to_vec()) + } +} +impl ReturnableErrorShape for dyn Display { + fn convert_to_returnable(&self, status_code: u16) -> TypedHttpResponse { + TypedHttpResponse::return_standard_error(status_code, MessageResource::new_from_err(self)) + } +} diff --git a/src/traits/mod.rs b/src/traits/mod.rs new file mode 100644 index 0000000..6783908 --- /dev/null +++ b/src/traits/mod.rs @@ -0,0 +1 @@ +pub mod macro_traits; \ No newline at end of file diff --git a/src/utils/macros.rs b/src/utils/macros.rs new file mode 100644 index 0000000..579ec43 --- /dev/null +++ b/src/utils/macros.rs @@ -0,0 +1,28 @@ +/// This is to minimize the amount of matches made in the code +/// Give it a Result 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) + } + } +} \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 7517331..42f93d9 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1 +1,2 @@ -pub mod logger_util; \ No newline at end of file +pub mod logger_util; +pub mod macros; \ No newline at end of file