Switched to new Err version & adding macros

This commit is contained in:
Franklin 2023-09-27 20:29:42 -04:00
parent 3cb538795b
commit ea1d463f33
15 changed files with 1154 additions and 162 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/actix-web-addons.iml" filepath="$PROJECT_DIR$/.idea/actix-web-addons.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

1014
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -15,4 +15,5 @@ actix-web = "4.1.0"
serde_json = { version = "1" }
serde = { version = "1.0", features = ["derive"] }
log = { version = "0.4", features = ["serde"] }
err = { git = "https://git.franklinblanco.dev/franklinblanco/err.git" }
err = { path = "../dev-deps/err" }
stdext = "0.3.1"

View File

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

View File

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

View File

@ -1,3 +1,4 @@
pub mod typed_response;
pub mod logger;
pub mod generic_error;
pub mod generic_error;
pub mod service_response;

View File

@ -0,0 +1,21 @@
use err::Error;
use serde::Serialize;
use crate::extensions::typed_response::TypedResponse;
pub trait IntoResponse<T> where T: Serialize {
fn to_response(self) -> TypedResponse<T>;
}
/// ## Type alias for Result<T, (StatusCode, Error)>.
/// Implements IntoResponse.
/// This is used to indicate the return type from a service function, but it is still usable as is.
pub type ServiceResponse<T> = Result<T, (u16, Error)>;
impl<T> IntoResponse<T> for ServiceResponse<T> where T: Serialize {
fn to_response(self) -> TypedResponse<T> {
match self {
Ok(response) => TypedResponse::std_response(200, response),
Err((status, error)) => TypedResponse::std_error(status, error),
}
}
}

View File

@ -1,74 +1,91 @@
use actix_web::{HttpResponse, http::StatusCode, web, HttpRequest, HttpResponseBuilder, body::BoxBody, Responder};
use serde::Serialize;
use err::MessageResource;
use err::{Error, MessageResource, Trace};
/// 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_utils::extensions::typed_response::TypedHttpResponse;
/// use actix_web_utils::dtos::message::MessageResource;
///
/// //Sample route
/// pub async fn testroute(number: Path<i32>) -> TypedHttpResponse<String> {
/// if(*number > 0){
/// return TypedHttpResponse::return_standard_response(StatusCode::OK, String::from("This is my test response!"));
/// }
/// TypedHttpResponse::return_empty_response(StatusCode::BAD_REQUEST)
/// }
///
/// ```
pub struct TypedHttpResponse<B: Serialize = String> {
pub response: HttpResponse<Option<web::Json<Result<B, Vec<MessageResource>>>>>,
pub struct TypedResponse<B: Serialize = String> {
pub response: HttpResponse<Option<web::Json<Result<B, Error>>>>,
}
impl<B: Serialize> TypedHttpResponse<B> {
impl<B: Serialize> TypedResponse<B> {
/// Returns a response with the json struct you define inside + Status code
/// ```
/// use actix_web_utils::extensions::typed_response::TypedHttpResponse;
/// use actix_web_utils::extensions::typed_response::TypedResponse;
///
/// TypedHttpResponse::return_standard_response(200, String::from("Anything in here")); //Instead of string you can put anything here as long as it is serializable.
/// TypedResponse::std_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: u16, body: B) -> TypedHttpResponse<B>{
TypedHttpResponse {
pub fn std_response(status_code: u16, body: B) -> TypedResponse<B>{
TypedResponse {
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: u16, body: Vec<MessageResource>) -> TypedHttpResponse<B>{
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: u16, body: MessageResource) -> TypedHttpResponse<B>{
TypedHttpResponse {
response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Err(vec![body]))))
pub fn std_error(status_code: u16, error: Error) -> TypedResponse<B>{
TypedResponse {
response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), Some(web::Json(Err(error))))
}
}
/// 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: u16) -> TypedHttpResponse<B>{
TypedHttpResponse {
pub fn empty_response(status_code: u16) -> TypedResponse<B>{
TypedResponse {
response: HttpResponse::with_body(StatusCode::from_u16(u16::from(status_code)).unwrap(), None)
}
}
}
impl<T: Serialize> Responder for TypedHttpResponse<T>
{
impl<T: Serialize> Responder for TypedResponse<T> {
type Body = BoxBody;
#[inline]
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
let mut builder = HttpResponseBuilder::new(self.response.status());
for header in self.response.headers() {
builder.append_header(header);
}
match self.response.body() {
Some(body) => {match body.0.as_ref() {
Some(body) => match body.0.as_ref() {
Ok(value) => builder.json(value),
Err(errors) => builder.json(errors),
}},
None => {builder.finish()}
},
None => builder.finish(),
}
}
}
impl<'a, T: Serialize> Responder for &'a TypedResponse<T> {
type Body = BoxBody;
#[inline]
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
let mut builder = HttpResponseBuilder::new(self.response.status());
for header in self.response.headers() {
builder.append_header(header);
}
match self.response.body() {
Some(body) => match body.0.as_ref() {
Ok(value) => builder.json(value),
Err(errors) => builder.json(errors),
},
None => builder.finish(),
}
}
}
impl<'a, T: Serialize> Responder for &'a mut TypedResponse<T> {
type Body = BoxBody;
#[inline]
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
let mut builder = HttpResponseBuilder::new(self.response.status());
for header in self.response.headers() {
builder.append_header(header);
}
match self.response.body() {
Some(body) => match body.0.as_ref() {
Ok(value) => builder.json(value),
Err(errors) => builder.json(errors),
},
None => builder.finish(),
}
}
}

View File

@ -1,4 +1,6 @@
pub mod extensions;
pub mod utils;
pub mod enums;
pub mod traits;
pub use extensions::typed_response::TypedResponse;
pub use extensions::service_response::ServiceResponse;
pub use stdext::function_name;

View File

@ -1,39 +0,0 @@
use std::fmt::Display;
use serde::Serialize;
use err::{Error, MessageResource};
use crate::{extensions::{typed_response::TypedHttpResponse, generic_error::GenericError}};
/// 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> {
match self {
Error::Unspecified => TypedHttpResponse::return_standard_error(status_code, MessageResource::from(self)),
Error::Network(message) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::UnexpectedStatusCode(_, actual, messages) => TypedHttpResponse::return_standard_error_list(*actual, messages.clone()),
Error::Serde(message) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::IO(message) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::Privilege(message) => TypedHttpResponse::return_standard_error(status_code, message.clone()),
Error::Parser(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<E: Display> ReturnableErrorShape for GenericError<E>{
fn convert_to_returnable<T: Serialize>(&self, status_code: u16) -> TypedHttpResponse<T> {
TypedHttpResponse::return_standard_error(status_code, MessageResource::new_from_str(&self.error.to_string()))
}
}

View File

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

View File

@ -1,40 +1,81 @@
/// This is to minimize the amount of matches made in the code
/// Give it a Result<Whatever_you_want_to_return, Error> and the type of the success 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.
/// Macro used to generate the trace object, must be called from the place where it originates, don't call from another function.
#[allow(unused_macros)]
#[macro_export]
macro_rules! unwrap_or_return_handled_error {
( $e:expr, $type_of_resp:ty ) => {
match $e {
Ok(value) => value,
Err(error) => return actix_web_utils::traits::macro_traits::ReturnableErrorShape::convert_to_returnable::<$type_of_resp>(&error, 500)
macro_rules! trace {
() => {
dtos::dtos::feature_less::message_resource::Trace {
line: line!(),
function: actix_web_utils::function_name!().into(),
file: file!().into(),
service: env!("CARGO_PKG_NAME").into(),
}
};
( $error_status_code:literal, $e:expr, $type_of_resp:ty ) => {
match $e {
Ok(value) => value,
Err(error) => return actix_web_utils::traits::macro_traits::ReturnableErrorShape::convert_to_returnable::<$type_of_resp>(&error, $error_status_code)
}
};
( $error_status_code:literal, $success_status_code:literal, $e:expr, $type_of_resp:ty) => {
match $e {
Ok(value) => return actix_web_utils::extensions::typed_response::TypedHttpResponse::return_standard_response($success_status_code, value),
Err(error) => return actix_web_utils::traits::macro_traits::ReturnableErrorShape::convert_to_returnable::<$type_of_resp>(&error, $error_status_code)
}
}
}
/// Takes whatever error you supply to it and wraps it in a GenericError<E> if err
/// # Unwrap result or return service response
/// Unwraps or turns Result<T, Error> -> ServiceResponse error variant
#[allow(unused_macros)]
#[macro_export]
macro_rules! wrap_generic_error_in_wrapper {
macro_rules! u_res_or_sr {
( $e:expr ) => {
match $e {
Ok(result) => result,
Err(error) => return Err((500, error.push_trace(actix_web_utils::trace!())))
}
};
( $e:expr, $status:expr ) => {
match $e {
Ok(result) => result,
Err(mut error) => return Err(($status, error.push_trace(macros::trace!())))
}
};
( $e:expr, $status:expr, $err:expr ) => {
match $e {
Ok(result) => result,
Err(mut error) => return Err(($status, error.push_error($err)))
}
};
}
/// # Unwrap option or return service response
/// Unwraps or turns Option<T> -> ServiceResponse error variant
#[allow(unused_macros)]
#[macro_export]
macro_rules! u_opt_or_sr {
( $e:expr ) => {
match $e {
Some(value) => value,
None => return Err((500, err::Error::new(actix_web_utils::trace!()).message("Option variant that was expected to be Option::Some(T) was Option::None.")))
}
};
}
/// Macro used to 'unwrap' a result that returns a Error
///
/// If there's an error returns the generated Error and push a trace on it.
#[allow(unused_macros)]
#[macro_export]
macro_rules! u_res_or_res {
( $e:expr ) => {
match $e {
Ok(result) => result,
Err(mut error) => return Err(error.push_trace(actix_web_utils::trace!()))
}
};
}
#[allow(unused_macros)]
#[macro_export]
macro_rules! x_e_res_or_db {
( $e:expr ) => {
match $e {
Ok(value) => Ok(value),
Err(error) => Err(actix_web_utils::extensions::generic_error::GenericError::wrap(error)),
Err(error) => {
return Err(RBError::new("database.on-read-write", macros::trace!()).typed(dtos::errors::error::RBErrorType::Database).message(error.to_string()).log_debug())
}
}
}
};
}