diff --git a/Cargo.lock b/Cargo.lock index c662e07..e9deb62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -96,6 +96,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "async-trait" version = "0.1.81" @@ -304,6 +310,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -354,7 +370,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "tiny-keccak", ] @@ -408,6 +424,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -823,7 +845,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -991,9 +1025,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "hyper" @@ -1324,7 +1358,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1394,7 +1428,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.5", "smallvec", "zeroize", ] @@ -1703,7 +1737,7 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand", + "rand 0.8.5", "sha2", "stringprep", ] @@ -1752,6 +1786,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "r2d2" version = "0.8.10" @@ -1780,8 +1820,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1791,7 +1841,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1800,7 +1860,36 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", +] + +[[package]] +name = "redis" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "438a4e5f8e9aa246d6f3666d6978441bf1b37d5f417b50c4dd220be09f5fcc17" +dependencies = [ + "arc-swap", + "combine", + "crc16", + "itoa", + "num-bigint", + "percent-encoding", + "r2d2", + "rand 0.9.1", + "ryu", + "sha1_smol", + "socket2", + "url", ] [[package]] @@ -1892,7 +1981,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -1924,7 +2013,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "spki", "subtle", @@ -2146,6 +2235,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "sha2" version = "0.10.8" @@ -2170,7 +2265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2337,7 +2432,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand", + "rand 0.8.5", "rsa", "serde", "sha1", @@ -2375,7 +2470,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand", + "rand 0.8.5", "serde", "serde_json", "sha2", @@ -2419,10 +2514,13 @@ dependencies = [ "diesel", "env_logger", "git2", + "humantime", "hyper", "lazy_static", "log", + "r2d2", "r2d2_postgres", + "redis", "reqwest", "serde", "serde_json", @@ -2666,7 +2764,7 @@ dependencies = [ "pin-project-lite", "postgres-protocol", "postgres-types", - "rand", + "rand 0.8.5", "socket2", "tokio", "tokio-util", @@ -2821,7 +2919,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.8.5", "sha1", "thiserror 1.0.63", "url", @@ -2956,6 +3054,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -3246,6 +3353,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index ba13da9..166e1f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,13 @@ config = "0.14.0" diesel = { version = "2.2.4", default-features = false, features = ["extras"], optional = true} env_logger = "0.11.5" git2 = {version = "0.18.2", optional = true} +humantime = "2.2.0" hyper = { version = "1.4.1"} lazy_static = "1.5.0" log = "0.4.22" +r2d2 = { version = "0.8.10", optional = true } r2d2_postgres = { version = "0.18.1", optional = true } +redis = { version = "0.30.0", features = ["cluster", "r2d2"], optional = true } reqwest = { version = "0.12.7", optional = true } serde = "1.0.208" serde_json = "1.0.125" @@ -32,3 +35,4 @@ git = ["dep:git2"] reqwest = ["dep:reqwest"] validator = ["dep:validator"] sqlx = ["dep:sqlx"] +dynerr = ["dep:redis", "dep:r2d2"] diff --git a/src/conf.rs b/src/conf.rs index 6cb97f7..4943311 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -1,6 +1,7 @@ -use std::collections::HashMap; +use std::{collections::HashMap, env, time::Duration}; use config::{Config, ConfigError, Environment}; +use humantime::parse_duration; use lazy_static::lazy_static; use serde::Deserialize; @@ -12,7 +13,7 @@ fn default_locale() -> String { fn default_yaml_path() -> String { "errors.yaml".to_string() } -pub fn default_error_messages() -> StandardErrorMessages{ +pub fn default_error_messages() -> StandardErrorMessages { [ ("ER-DB-NOTFOUND", "Record not found: [err]"), ("ER-DB-DATABASE", "Database error: [err]"), @@ -36,30 +37,84 @@ pub fn default_error_messages() -> StandardErrorMessages{ ("ER-IO-INTERRUPTED", "Operation interrupted: [err]"), ("ER-IO-UNEXPECTEDEOF", "Unexpected end of file: [err]"), ("ER-IO-UNKNOWN", "An unknown I/O error occurred: [err]"), - ("ERR-GIT-GENERIC", "A generic error occurred in the Git operation: [err]"), - ("ERR-GIT-NOTFOUND", "The requested resource was not found in the Git repository: [err]"), - ("ERR-GIT-EXISTS", "The resource already exists in the Git repository: [err]"), + ( + "ERR-GIT-GENERIC", + "A generic error occurred in the Git operation: [err]", + ), + ( + "ERR-GIT-NOTFOUND", + "The requested resource was not found in the Git repository: [err]", + ), + ( + "ERR-GIT-EXISTS", + "The resource already exists in the Git repository: [err]", + ), ("ERR-GIT-AMBIGUOUS", "The Git reference is ambiguous: [err]"), - ("ERR-GIT-BUFSIZE", "Buffer size is insufficient for the Git operation: [err]"), - ("ERR-GIT-USER", "User-defined error encountered in the Git operation: [err]"), - ("ERR-GIT-BARE-REPO", "Operation cannot be performed on a bare Git repository: [err]"), - ("ERR-GIT-UNBORN-BRANCH", "The branch has not been created yet: [err]"), - ("ERR-GIT-UNMERGED", "There are unmerged changes in the Git repository: [err]"), - ("ERR-GIT-NOT-FAST-FORWARD", "The branch is not fast-forwardable: [err]"), - ("ERR-GIT-INVALID-SPEC", "The Git specification provided is invalid: [err]"), - ("ERR-GIT-CONFLICT", "A conflict occurred during the Git operation: [err]"), + ( + "ERR-GIT-BUFSIZE", + "Buffer size is insufficient for the Git operation: [err]", + ), + ( + "ERR-GIT-USER", + "User-defined error encountered in the Git operation: [err]", + ), + ( + "ERR-GIT-BARE-REPO", + "Operation cannot be performed on a bare Git repository: [err]", + ), + ( + "ERR-GIT-UNBORN-BRANCH", + "The branch has not been created yet: [err]", + ), + ( + "ERR-GIT-UNMERGED", + "There are unmerged changes in the Git repository: [err]", + ), + ( + "ERR-GIT-NOT-FAST-FORWARD", + "The branch is not fast-forwardable: [err]", + ), + ( + "ERR-GIT-INVALID-SPEC", + "The Git specification provided is invalid: [err]", + ), + ( + "ERR-GIT-CONFLICT", + "A conflict occurred during the Git operation: [err]", + ), ("ERR-GIT-LOCKED", "The Git resource is locked: [err]"), ("ERR-GIT-MODIFIED", "The file has been modified: [err]"), - ("ERR-GIT-AUTH", "Authentication failed during the Git operation: [err]"), - ("ERR-GIT-CERTIFICATE", "Certificate validation failed during the Git operation: [err]"), - ("ERR-GIT-APPLIED", "The patch has already been applied: [err]"), + ( + "ERR-GIT-AUTH", + "Authentication failed during the Git operation: [err]", + ), + ( + "ERR-GIT-CERTIFICATE", + "Certificate validation failed during the Git operation: [err]", + ), + ( + "ERR-GIT-APPLIED", + "The patch has already been applied: [err]", + ), ("ERR-GIT-PEEL", "Peeling operation failed: [err]"), ("ERR-GIT-EOF", "Unexpected end of file encountered: [err]"), - ("ERR-GIT-INVALID", "An invalid operation was attempted: [err]"), - ("ERR-GIT-UNCOMMITTED", "There are uncommitted changes: [err]"), - ("ERR-GIT-DIRECTORY", "The directory is invalid or not found: [err]"), + ( + "ERR-GIT-INVALID", + "An invalid operation was attempted: [err]", + ), + ( + "ERR-GIT-UNCOMMITTED", + "There are uncommitted changes: [err]", + ), + ( + "ERR-GIT-DIRECTORY", + "The directory is invalid or not found: [err]", + ), ("ERR-GIT-MERGE-CONFLICT", "A merge conflict occurred: [err]"), - ("ERR-GIT-HASHSUM-MISMATCH", "Hashsum mismatch detected: [err]"), + ( + "ERR-GIT-HASHSUM-MISMATCH", + "Hashsum mismatch detected: [err]", + ), ("ERR-GIT-INDEX-DIRTY", "The Git index is dirty: [err]"), ("ERR-GIT-APPLY-FAIL", "Failed to apply the patch: [err]"), ("ERR-GIT-OWNER", "Invalid owner in the Git operation: [err]"), @@ -105,6 +160,50 @@ impl Settings { } } +pub struct RedisSettings { + pub use_redis_cluster: bool, + pub cache_location: String, + pub cache_timeout: Duration +} + +#[derive(Deserialize)] +pub struct RedisSettingsHelper{ + pub use_redis_cluster: bool, + pub cache_location: String, +} + +impl<'de> Deserialize<'de> for RedisSettings{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de> { + let conf = RedisSettingsHelper::deserialize(deserializer)?; + let timeout_str = env::var("CACHE_TIMEOUT").unwrap_or_else(|_| "5m".to_string()); + let cache_timeout = match parse_duration(&timeout_str) { + Ok(dur) => dur, + Err(_) => { + match timeout_str.parse::() { + Ok(secs) => Duration::from_secs(secs), + Err(_) => Duration::from_secs(300), + } + } + }; + Ok(Self{ + use_redis_cluster: conf.use_redis_cluster, + cache_location: conf.cache_location, + cache_timeout + }) + } +} + +impl RedisSettings { + pub fn new() -> Result { + let conf = Config::builder() + .add_source(Environment::default()) + .build()?; + conf.try_deserialize() + } +} + lazy_static! { pub static ref error_messages: StandardErrorMessages = StandardError::load_error_messages().expect("error loading error csv"); diff --git a/src/extras/dynerr/mod.rs b/src/extras/dynerr/mod.rs new file mode 100644 index 0000000..74aa983 --- /dev/null +++ b/src/extras/dynerr/mod.rs @@ -0,0 +1,28 @@ +#[cfg(feature = "dynerr")] +mod redis; + +use redis::RedisBackend; + +#[cfg(feature = "dynerr")] +use crate::StandardError; + +#[cfg(feature = "dynerr")] +pub trait DynErr { + fn dyn_err(&mut self, prefix: &str) -> Self; +} + +#[cfg(feature = "dynerr")] +impl DynErr for StandardError { + fn dyn_err(&mut self, key: &str) -> Self { + match RedisBackend::new(){ + Ok(client) => { + if let Ok(Some(msg)) = client.get::(&key){ + self.message = msg; + log::info!("using dynamic message: {}", &self.message); + } + }, + Err(e) => {return e;} + } + self.clone() + } +} diff --git a/src/extras/dynerr/redis.rs b/src/extras/dynerr/redis.rs new file mode 100644 index 0000000..05a7dba --- /dev/null +++ b/src/extras/dynerr/redis.rs @@ -0,0 +1,68 @@ +#[cfg(feature="dynerr")] +use r2d2::Pool; + +#[cfg(feature="dynerr")] +use redis::cluster::ClusterClient; + +#[cfg(feature="dynerr")] +use redis::{Client, FromRedisValue, RedisError, Commands}; + +#[cfg(feature="dynerr")] +use crate::{redis_settings, Interpolate, StandardError, Result}; + +#[cfg(feature="dynerr")] +impl From for StandardError{ + fn from(err: RedisError) -> Self { + StandardError::new("ER-REDIS").interpolate_err(format!("{:?}", &err)) + } +} + +#[cfg(all(feature = "dynerr", not(feature = "diesel")))] +impl From for StandardError{ + fn from(err: r2d2::Error) -> Self { + StandardError::new("ER-REDIS").interpolate_err(format!("{:?}", &err)) + } +} + + +#[derive(Clone)] +pub enum RedisBackend { + Redis(Pool), + RedisCluster(Pool), +} + +impl RedisBackend { + pub fn new() -> Result { + if redis_settings.use_redis_cluster { + let nodes = vec![redis_settings.cache_location.clone()]; + let cluster_client = ClusterClient::new(nodes)?; + let cluster_pool = Pool::builder() + .max_size(5) + .connection_timeout(redis_settings.cache_timeout) + .build(cluster_client)?; + Ok(RedisBackend::RedisCluster(cluster_pool)) + } else { + let client = Client::open(redis_settings.cache_location.clone())?; + let pool = Pool::builder() + .max_size(5) + .connection_timeout(redis_settings.cache_timeout) + .build(client)?; + Ok(RedisBackend::Redis(pool)) + } + } + + pub fn get(&self, key: &str) -> Result> { + match self { + RedisBackend::Redis(pool) => { + let mut conn = pool.get()?; + let value = redis::cmd("GET").arg(key).query::>(&mut *conn)?; + Ok(value) + } + RedisBackend::RedisCluster(pool) => { + let mut conn = pool.get()?; + let value = redis::cmd("GET").arg(key).query::>(&mut *conn)?; + Ok(value) + } + } + } +} diff --git a/src/extras/fromerrs/axum.rs b/src/extras/fromerrs/axum.rs index d10a954..5b969a1 100644 --- a/src/extras/fromerrs/axum.rs +++ b/src/extras/fromerrs/axum.rs @@ -1,15 +1,21 @@ -use axum::http::{StatusCode, Error as HttpError}; -use crate::{StandardError, Interpolate}; +use crate::{Interpolate, StandardError}; +use axum::http::{Error as HttpError, StatusCode}; use std::error::Error as StdError; impl From for StandardError { fn from(error: axum::Error) -> Self { log::error!("axum error: {}", &error.to_string()); - if let Some(io_err) = error.source().and_then(|e| e.downcast_ref::()) { + if let Some(io_err) = error + .source() + .and_then(|e| e.downcast_ref::()) + { StandardError::new("ER-AXUM-IO") .interpolate_err(format!("IO error occurred: {}", io_err)) - } else if let Some(hyper_err) = error.source().and_then(|e| e.downcast_ref::()) { + } else if let Some(hyper_err) = error + .source() + .and_then(|e| e.downcast_ref::()) + { StandardError::new("ER-AXUM-HYPER") .interpolate_err(format!("Hyper error occurred: {}", hyper_err)) } else if let Some(http_err) = error.source().and_then(|e| e.downcast_ref::()) { @@ -31,8 +37,9 @@ impl From for StandardError { .interpolate_err("Resource not found".to_string()), StatusCode::INTERNAL_SERVER_ERROR => StandardError::new("ER-AXUM-INTERNAL") .interpolate_err("Internal server error".to_string()), - StatusCode::BAD_REQUEST => StandardError::new("ER-AXUM-BADREQUEST") - .interpolate_err("Bad request".to_string()), + StatusCode::BAD_REQUEST => { + StandardError::new("ER-AXUM-BADREQUEST").interpolate_err("Bad request".to_string()) + } StatusCode::FORBIDDEN => StandardError::new("ER-AXUM-FORBIDDEN") .interpolate_err("Forbidden request".to_string()), StatusCode::UNAUTHORIZED => StandardError::new("ER-AXUM-UNAUTHORIZED") @@ -62,7 +69,6 @@ impl From for StandardError { impl From for StandardError { fn from(error: axum::http::Error) -> Self { log::error!("HTTP error: {}", &error.to_string()); - StandardError::new("ER-AXUM-HTTPERROR") - .interpolate_err("General HTTP error".to_string()) + StandardError::new("ER-AXUM-HTTPERROR").interpolate_err("General HTTP error".to_string()) } } diff --git a/src/extras/fromerrs/database/diesel.rs b/src/extras/fromerrs/database/diesel.rs index b222ae0..13702da 100644 --- a/src/extras/fromerrs/database/diesel.rs +++ b/src/extras/fromerrs/database/diesel.rs @@ -1,15 +1,15 @@ -#[cfg(feature="diesel")] +use crate::{Interpolate, StandardError}; +#[cfg(feature = "diesel")] use diesel::result::Error as DieselError; -use crate::{StandardError, Interpolate}; - -#[cfg(feature="diesel")] +#[cfg(feature = "diesel")] impl From for StandardError { fn from(error: DieselError) -> Self { - log::error!("db error: {}", &error.to_string()); + log::error!("db error: {}", &error.to_string()); match error { - DieselError::NotFound => StandardError::new("ER-DB-NOTFOUND") - .interpolate_err("Record not found".to_string()), + DieselError::NotFound => { + StandardError::new("ER-DB-NOTFOUND").interpolate_err("Record not found".to_string()) + } DieselError::DatabaseError(_, info) => StandardError::new("ER-DB-DATABASE") .interpolate_err(format!("Database error: {}", info.message())), DieselError::QueryBuilderError(_) => StandardError::new("ER-DB-QUERYBUILDER") @@ -25,5 +25,3 @@ impl From for StandardError { } } } - - diff --git a/src/extras/fromerrs/database/mod.rs b/src/extras/fromerrs/database/mod.rs index d468ea9..21793dc 100644 --- a/src/extras/fromerrs/database/mod.rs +++ b/src/extras/fromerrs/database/mod.rs @@ -1,3 +1,3 @@ -pub mod sqlx; pub mod diesel; pub mod r2d2_postgres; +pub mod sqlx; diff --git a/src/extras/fromerrs/database/r2d2_postgres.rs b/src/extras/fromerrs/database/r2d2_postgres.rs index 6fc411b..7f522cf 100644 --- a/src/extras/fromerrs/database/r2d2_postgres.rs +++ b/src/extras/fromerrs/database/r2d2_postgres.rs @@ -1,9 +1,8 @@ -use crate::{StandardError, Interpolate}; -#[cfg(feature="diesel")] -use r2d2_postgres::r2d2::Error; +use crate::{Interpolate, StandardError}; +#[cfg(feature = "diesel")] +use r2d2_postgres::r2d2::Error; - -#[cfg(feature="diesel")] +#[cfg(feature = "diesel")] impl From for StandardError { fn from(error: Error) -> Self { StandardError::new("ER-DB-POOL").interpolate_err(error.to_string()) diff --git a/src/extras/fromerrs/database/sqlx.rs b/src/extras/fromerrs/database/sqlx.rs index 8caf5a8..653c6ea 100644 --- a/src/extras/fromerrs/database/sqlx.rs +++ b/src/extras/fromerrs/database/sqlx.rs @@ -1,14 +1,15 @@ -#[cfg(feature="sqlx")] +use crate::{Interpolate, StandardError}; +#[cfg(feature = "sqlx")] use sqlx::error::Error as SqlxError; -use crate::{StandardError, Interpolate}; -#[cfg(feature="sqlx")] +#[cfg(feature = "sqlx")] impl From for StandardError { fn from(error: SqlxError) -> Self { log::error!("db error: {}", &error.to_string()); match error { - SqlxError::RowNotFound => StandardError::new("ER-DB-NOTFOUND") - .interpolate_err("Record not found".to_string()), + SqlxError::RowNotFound => { + StandardError::new("ER-DB-NOTFOUND").interpolate_err("Record not found".to_string()) + } SqlxError::Database(db_err) => StandardError::new("ER-DB-DATABASE") .interpolate_err(format!("Database error: {}", db_err.message())), SqlxError::PoolTimedOut => StandardError::new("ER-DB-POOLTIMEOUT") diff --git a/src/extras/fromerrs/git.rs b/src/extras/fromerrs/git.rs index 25ff068..3ebb8cc 100644 --- a/src/extras/fromerrs/git.rs +++ b/src/extras/fromerrs/git.rs @@ -1,9 +1,7 @@ +#[cfg(feature = "git")] +use crate::{Interpolate, StandardError}; -#[cfg(feature="git")] -use crate::{StandardError, Interpolate}; - - -#[cfg(feature="git")] +#[cfg(feature = "git")] impl From for StandardError { fn from(error: git2::Error) -> Self { let code = match error.code() { diff --git a/src/extras/fromerrs/mod.rs b/src/extras/fromerrs/mod.rs index 0ee326d..8eb1134 100644 --- a/src/extras/fromerrs/mod.rs +++ b/src/extras/fromerrs/mod.rs @@ -1,6 +1,6 @@ -pub mod database; -pub mod serde; pub mod axum; +pub mod database; pub mod git; -pub mod stdio; pub mod reqwest; +pub mod serde; +pub mod stdio; diff --git a/src/extras/fromerrs/reqwest.rs b/src/extras/fromerrs/reqwest.rs index 3839b5e..6e7ec98 100644 --- a/src/extras/fromerrs/reqwest.rs +++ b/src/extras/fromerrs/reqwest.rs @@ -1,7 +1,7 @@ #[cfg(feature = "reqwest")] use reqwest::Error as ReqwestError; -use crate::{StandardError, Interpolate}; +use crate::{Interpolate, StandardError}; #[cfg(feature = "reqwest")] impl From for StandardError { diff --git a/src/extras/fromerrs/serde.rs b/src/extras/fromerrs/serde.rs index 2d7ee58..2d385c2 100644 --- a/src/extras/fromerrs/serde.rs +++ b/src/extras/fromerrs/serde.rs @@ -2,25 +2,22 @@ use serde::de::value::Error as SerdeError; use serde_json::Error as SerdeJsonError; use serde_yaml::Error as SerdeYamlError; -use crate::{StandardError, Interpolate}; - +use crate::{Interpolate, StandardError}; impl From for StandardError { fn from(err: SerdeError) -> Self { - StandardError::new("ER-SERDE") - .interpolate_err(err.to_string()) + StandardError::new("ER-SERDE").interpolate_err(err.to_string()) } } impl From for StandardError { fn from(err: SerdeJsonError) -> Self { - StandardError::new("ER-SERDE-JSON") - .interpolate_err(err.to_string()) + StandardError::new("ER-SERDE-JSON").interpolate_err(err.to_string()) } } impl From for StandardError { fn from(err: SerdeYamlError) -> Self { - StandardError::new("ER-SERDE-YAML") - .interpolate_err(err.to_string()) } + StandardError::new("ER-SERDE-YAML").interpolate_err(err.to_string()) + } } diff --git a/src/extras/fromerrs/stdio.rs b/src/extras/fromerrs/stdio.rs index 1ef4f5b..3b3b70b 100644 --- a/src/extras/fromerrs/stdio.rs +++ b/src/extras/fromerrs/stdio.rs @@ -1,5 +1,5 @@ +use crate::{Interpolate, StandardError}; use std::io; -use crate::{StandardError, Interpolate}; impl From for StandardError { fn from(error: io::Error) -> Self { diff --git a/src/extras/interpolate.rs b/src/extras/interpolate.rs index 89a8fea..90bc806 100644 --- a/src/extras/interpolate.rs +++ b/src/extras/interpolate.rs @@ -2,12 +2,12 @@ use std::collections::HashMap; use crate::StandardError; -pub trait Interpolate{ +pub trait Interpolate { fn interpolate_values(&mut self, values: HashMap) -> Self; fn interpolate_err(&mut self, e: String) -> Self; } -impl Interpolate for StandardError{ +impl Interpolate for StandardError { fn interpolate_values(&mut self, values: HashMap) -> Self { let mut new_message = self.message.clone(); // Clone the message to avoid mutating it directly for (k, v) in values.into_iter() { @@ -22,5 +22,4 @@ impl Interpolate for StandardError{ values.insert("err".to_string(), e); self.interpolate_values(values) } - } diff --git a/src/extras/mod.rs b/src/extras/mod.rs index 1e5ac40..137aa25 100644 --- a/src/extras/mod.rs +++ b/src/extras/mod.rs @@ -1,4 +1,5 @@ +pub mod dynerr; +pub mod fromerrs; pub mod interpolate; pub mod response; pub mod status; -pub mod fromerrs; diff --git a/src/extras/response.rs b/src/extras/response.rs index f35fc15..f015117 100644 --- a/src/extras/response.rs +++ b/src/extras/response.rs @@ -1,15 +1,11 @@ use crate::StandardError; use axum::response::IntoResponse; -use axum::Json; use axum::response::Response; +use axum::Json; use serde_json::json; impl IntoResponse for StandardError { fn into_response(self) -> Response { - ( - self.status_code, - Json(json!({"detail": self.message})) - ) - .into_response() + (self.status_code, Json(json!({"detail": self.message}))).into_response() } } diff --git a/src/extras/status.rs b/src/extras/status.rs index 9321bf1..6f1a78b 100644 --- a/src/extras/status.rs +++ b/src/extras/status.rs @@ -2,17 +2,13 @@ use axum::http::StatusCode; use crate::StandardError; - - -pub trait Status{ +pub trait Status { fn code(&mut self, code: StatusCode) -> Self; } -impl Status for StandardError{ - fn code(&mut self, code: StatusCode) -> Self{ +impl Status for StandardError { + fn code(&mut self, code: StatusCode) -> Self { self.status_code = code; self.clone() } } - - diff --git a/src/lib.rs b/src/lib.rs index bd6b3f9..6bccf1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,17 +4,18 @@ use std::collections::HashMap; use thiserror::Error; mod conf; +pub mod extras; mod loader; mod locale; -pub mod extras; pub use locale::get_current_locale; pub use locale::set_current_locale; - pub type StandardErrorMessages = HashMap>; -pub use extras::status::Status; pub use extras::interpolate::Interpolate; +pub use extras::status::Status; + +pub type Result = core::result::Result; #[derive(Debug, Clone, Error)] #[error("Error {err_code} with status {status_code}")] @@ -34,37 +35,40 @@ impl StandardError { message: error_messages .get(code) .and_then(|locale_message| locale_message.get(&locale::get_current_locale())) - .map_or_else(|| format!("unknown error: {}", &code), |msg| msg.to_string()), + .map_or_else( + || format!("unknown error: {}", &code), + |msg| msg.to_string(), + ), } } } - lazy_static! { pub static ref settings: conf::Settings = conf::Settings::new().expect("improperly configured"); + #[cfg(feature = "dynerr")] + pub static ref redis_settings: conf::RedisSettings = conf::RedisSettings::new().expect("improperly configured"); pub static ref error_messages: StandardErrorMessages = StandardError::load_error_messages().expect("error loading error messages"); } #[cfg(test)] mod tests { - use std::{collections::HashMap, num::ParseIntError}; - use crate::extras::{status::Status, interpolate::Interpolate}; + use crate::extras::{interpolate::Interpolate, status::Status}; use axum::http::StatusCode; + use std::{collections::HashMap, num::ParseIntError}; use crate::StandardError; #[tokio::test] async fn test_question_mark() -> Result<(), StandardError> { async fn foo(a: &str) -> Result { - a.parse().map_err(|_: ParseIntError| { - StandardError::new("ER-0004") - }) + a.parse() + .map_err(|_: ParseIntError| StandardError::new("ER-0004")) } let res = foo("a").await; - if let Err(e) = res{ + if let Err(e) = res { assert_eq!(e.status_code, StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(e.message, "Should be an integer".to_string()) } @@ -82,7 +86,7 @@ mod tests { let res = foo("a").await; - if let Err(e) = res{ + if let Err(e) = res { assert_eq!(e.status_code, StatusCode::BAD_REQUEST); assert_eq!(e.message, "Should be an integer".to_string()) } @@ -100,9 +104,12 @@ mod tests { let res = foo("a").await; - if let Err(e) = res{ + if let Err(e) = res { assert_eq!(e.status_code, StatusCode::INTERNAL_SERVER_ERROR); - assert_eq!(e.message, "Should be an integer: invalid digit found in string".to_string()) + assert_eq!( + e.message, + "Should be an integer: invalid digit found in string".to_string() + ) } Ok(()) @@ -121,9 +128,12 @@ mod tests { let res = foo("a").await; - if let Err(e) = res{ + if let Err(e) = res { assert_eq!(e.status_code, StatusCode::INTERNAL_SERVER_ERROR); - assert_eq!(e.message, "Should be an integer - fname: ashu | lname: pednekar".to_string()) + assert_eq!( + e.message, + "Should be an integer - fname: ashu | lname: pednekar".to_string() + ) } Ok(()) @@ -145,14 +155,11 @@ mod tests { let res = foo("a").await; - if let Err(e) = res{ + if let Err(e) = res { assert_eq!(e.status_code, StatusCode::IM_A_TEAPOT); assert_eq!(e.message, "Should be an integer - fname: ashu | lname: pednekar - invalid digit found in string".to_string()) } Ok(()) } - - - } diff --git a/src/loader.rs b/src/loader.rs index d7e75f2..91b43c9 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -8,7 +8,7 @@ impl StandardError { let file = File::open(&settings.error_yaml_file_path)?; let reader = BufReader::new(file); let yaml: Value = serde_yaml::from_reader(reader)?; - let mut messages: StandardErrorMessages = default_error_messages(); + let mut messages: StandardErrorMessages = default_error_messages(); if let Some(errors) = yaml.get("errors").and_then(|v| v.as_sequence()) { for error in errors { if let Some(code) = error.get("code").and_then(|v| v.as_str()) {