Adapted err library, dtos and enums to fit Kotlin and Swift data types

This commit is contained in:
Franklin 2023-10-27 09:31:22 -04:00
parent 18cc77b626
commit 68dfdca2a7
4 changed files with 116 additions and 27 deletions

View File

@ -14,4 +14,7 @@ repository = "https://github.com/franklinblanco/err.git"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0.48" thiserror = "1.0.48"
sqlx = { version = "0.7", features = ["json"] } sqlx = { version = "0.7", features = ["json"] }
stdext = "0.3.1" stdext = "0.3.1"
[dev-dependencies]
serde_json = { version = "1" }

View File

@ -1,4 +1,5 @@
mod macros; mod macros;
mod tests;
use std::fmt::Display; use std::fmt::Display;
use serde::{Serialize, Deserialize, Serializer}; use serde::{Serialize, Deserialize, Serializer};
@ -7,6 +8,7 @@ use thiserror::Error;
pub use stdext::function_name; pub use stdext::function_name;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Trace { pub struct Trace {
pub line: u32, pub line: u32,
@ -21,7 +23,7 @@ impl Trace {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Traces(pub Vec<Trace>); pub struct Traces { pub traces: Vec<Trace> }
#[derive(Serialize, Debug, Error)] #[derive(Serialize, Debug, Error)]
@ -37,17 +39,18 @@ pub struct Error {
impl Error { impl Error {
pub fn new(trace: Trace) -> Self { pub fn new(trace: Trace) -> Self {
Self { Self {
trace: Traces(Vec::from([trace])), trace: Traces { traces: Vec::from([trace]) },
message_resource: MessageResource::new("errors.backend.common.default", "We still don't have an error defined for this."), message_resource: MessageResource::new("errors.backend.common.default", "We still don't have an error defined for this."),
error_type: ErrorType::Unspecified, error_type: ErrorType::Unspecified,
} }
} }
pub fn push_trace(mut self, trace: Trace) -> Self { pub fn push_trace(mut self, trace: Trace) -> Self {
self.trace.0.push(trace); self.trace.traces.push(trace);
self self
} }
pub fn push_error(mut self, error: Error) -> Self { pub fn push_error(mut self, error: Error) -> Self {
self.error_type = ErrorType::Nested(Box::new(error)); self.error_type = ErrorType::Nested { nested: Box::new(error) };
self self
} }
pub fn key(mut self, key: Option<impl ToString>) -> Self { pub fn key(mut self, key: Option<impl ToString>) -> Self {
@ -77,11 +80,13 @@ pub struct MessageResource {
pub key: Option<String>, pub key: Option<String>,
pub message: String, pub message: String,
} }
impl Display for MessageResource { impl Display for MessageResource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MessageResource Key: {:#?}, Message: {}", self.key, self.message) write!(f, "MessageResource Key: {:#?}, Message: {}", self.key, self.message)
} }
} }
impl MessageResource { impl MessageResource {
pub fn new(key: &str, msg: &str) -> Self { pub fn new(key: &str, msg: &str) -> Self {
Self { key: Some(key.to_string()), message: msg.to_string() } Self { key: Some(key.to_string()), message: msg.to_string() }
@ -115,20 +120,20 @@ pub enum ErrorType {
IO, IO,
#[error("Privilege Error")] #[error("Privilege Error")]
Privilege, Privilege,
#[error("Unexpected Status Code. Expected: {0} Actual: {1}")] #[error("Unexpected Status Code. Expected: {expected} Actual: {actual}")]
UnexpectedStatusCode(u16, u16), UnexpectedStatusCode { expected: u16, actual: u16 },
#[error("Serde Error. Attempted to Serialize/Deserialize String: {0}")] #[error("Serde Error. Attempted to Serialize/Deserialize String: {text}")]
Serde(String), Serde { text: String },
#[error("Parsing error.")] #[error("Parsing error.")]
Parser, Parser,
#[error("Service Error: {0}")] #[error("Service Error: {error}")]
Service(ServiceError), Service { error: ServiceError },
#[error("Unspecified Error")] #[error("Unspecified Error")]
Unspecified, Unspecified,
#[error("Unexpected Error: {0}")] #[error("Unexpected Error: {message}")]
Unexpected(String), Unexpected { message: String },
#[error("Nested Error: {0:?}")] #[error("Nested Error: {nested}")]
Nested(Box<Error>), Nested { nested: Box<Error> },
} }
#[derive(Error, Serialize, Debug)] #[derive(Error, Serialize, Debug)]
@ -137,28 +142,33 @@ pub enum ServiceError {
#[error("Error parsing string into value")] #[error("Error parsing string into value")]
FromStrError, FromStrError,
/// Every error that is returned from a DAO operation. /// Every error that is returned from a DAO operation.
#[error("Error from the Database: {0}")] #[error("Error from the Database: {error}")]
#[serde(serialize_with = "ser_with")] #[serde(serialize_with = "ser_with")]
DatabaseError(#[from] sqlx::Error), DatabaseError {
#[from]
error: sqlx::Error
},
/// A vec of ValidationErrors /// A vec of ValidationErrors
#[error("Validation Errors: {0:?}")] #[error("Error Operation Not Allowed: {message}")]
ValidationErrors(Vec<ValidationError>), NotAllowed { message: String },
#[error("Validation Errors: {errors:?}")]
ValidationErrors { errors: Vec<ValidationError> },
/// Something already exists. That something should be {0} /// Something already exists. That something should be {0}
/// Example: "User" "Credential" /// Example: "User" "Credential"
#[error("Error {0} Already exists.")] #[error("Error {message} Already exists.")]
AlreadyExistsError(String), AlreadyExistsError { message: String },
/// Example: "User with id X" /// Example: "User with id X"
#[error("{0} Not found.")] #[error("{message} Not found.")]
NotFoundError(String), NotFoundError { message: String },
/// Used to specify authentication error. /// Used to specify authentication error.
/// Example: Password incorrect for user /// Example: Password incorrect for user
#[error("Credential supplied is incorrect. {0}")] #[error("Credential supplied is incorrect. {message}")]
IncorrectCredentialError(String), IncorrectCredentialError { message: String },
#[error("Too many credentials supplied, maximum is 3.")] #[error("Too many credentials supplied, maximum is 3.")]
TooManyCredentialsError, TooManyCredentialsError,
/// Used for anything else. /// Used for anything else.
#[error("Unexpected Error: {0}")] #[error("Unexpected Error: {message}")]
UnexpectedError(String), UnexpectedError { message: String },
} }
/// Any string validation error such as Phone number validation or email, etc... /// Any string validation error such as Phone number validation or email, etc...
@ -176,4 +186,21 @@ pub fn ser_with<S>(id: &sqlx::Error, s: S) -> Result<S::Ok, S::Error> where
let mut ser = s.serialize_map(Some(1))?; let mut ser = s.serialize_map(Some(1))?;
ser.serialize_entry("$oid", &id.to_string())?; ser.serialize_entry("$oid", &id.to_string())?;
ser.end() ser.end()
}
pub trait VecRemove {
type T;
fn try_remove(&mut self, index: usize) -> Option<Self::T>;
}
impl<T> VecRemove for Vec<T> {
type T = T;
fn try_remove(&mut self, index: usize) -> Option<Self::T> {
if index < self.len() {
Some(self.remove(index))
} else {
None
}
}
} }

View File

@ -12,6 +12,20 @@ macro_rules! trace {
}; };
} }
/// 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! test_trace {
() => {
crate::Trace {
line: line!(),
function: crate::function_name!().into(),
file: file!().into(),
service: env!("CARGO_PKG_NAME").into(),
}
};
}
/// Macro used to 'unwrap' a result that returns a Error /// 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. /// If there's an error returns the generated Error and push a trace on it.

45
src/tests.rs Normal file
View File

@ -0,0 +1,45 @@
#[cfg(test)]
mod tests {
use crate::{Error, ErrorType, ServiceError, test_trace};
#[test]
fn print_json() {
let error = Error::new(test_trace!())
.message("Hi there g")
.push_trace(test_trace!())
.key(Some("This key"))
.push_error(
Error::new(test_trace!())
.error_type(ErrorType::Service { error: ServiceError::AlreadyExistsError { message: String::from("Hey now") } })
.message("Hi there g")
.push_trace(test_trace!())
.key(Some("key for nested err"))
);
println!("Object in rust: {:#?}", error);
println!("");
println!("#############################################");
println!("");
println!("Object in json: {}", serde_json::to_string_pretty(&error).unwrap())
}
#[test]
fn print_json_with_db_error_variant() {
let error = Error::new(test_trace!())
.message("Hi there g")
.push_trace(test_trace!())
.key(Some("This key"))
.push_error(
Error::new(test_trace!())
.error_type(ErrorType::Service { error: ServiceError::DatabaseError { error: sqlx::Error::Protocol("AAAA".into()) } })
.message("Hi there g")
.push_trace(test_trace!())
.key(Some("key for nested err"))
);
println!("Object in rust: {:#?}", error);
println!("");
println!("#############################################");
println!("");
println!("Object in json: {}", serde_json::to_string_pretty(&error).unwrap())
}
}