diff --git a/Cargo.lock b/Cargo.lock index 754805a..5458cb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -348,6 +348,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.27.3" @@ -379,6 +390,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.1" @@ -600,9 +617,11 @@ dependencies = [ "lazy-regex", "serde", "serde_json", + "strum_macros", "tokio", "tower-cookies", "tower-http", + "uuid", ] [[package]] @@ -841,6 +860,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.63" @@ -875,6 +900,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1131,6 +1186,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "syn" version = "2.0.22" @@ -1416,6 +1484,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", + "rand", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index d32e180..330aaa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ tower-cookies = "0.9" # Others lazy-regex = "3" async-trait = "0.1" +strum_macros = "0.25" +uuid = { version = "1", features = ["v4", "fast-rng"] } [dev-dependencies] anyhow = "1" diff --git a/src/error.rs b/src/error.rs index b46c2ed..8452bab 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,7 +3,7 @@ use axum::response::{IntoResponse, Response}; pub type Result = core::result::Result; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, strum_macros::AsRefStr)] pub enum Error { LoginFail, @@ -16,11 +16,50 @@ pub enum Error { PropertyDeleteFailIdNotFound { id: u64 }, } -/// FIXME: return different status codes for different errors +impl Error { + pub fn client_status_and_error(&self) -> (StatusCode, ClientError) { + match self { + // -- Login. + Self::LoginFail => (StatusCode::UNAUTHORIZED, ClientError::LOGIN_FAIL), + + // -- Auth. + Self::AuthFailNoAuthTokenCookie + | Self::AuthFailTokenWrongFormat + | Self::AuthFailCtxNotInRequestExt => (StatusCode::FORBIDDEN, ClientError::NO_AUTH), + + // -- Model. + Self::PropertyDeleteFailIdNotFound { .. } => { + (StatusCode::BAD_REQUEST, ClientError::INVALID_PARAMS) + } + + // -- Fallback. + _ => ( + StatusCode::INTERNAL_SERVER_ERROR, + ClientError::SERVICE_ERROR, + ), + } + } +} + impl IntoResponse for Error { fn into_response(self) -> Response { println!("->> {:<12} - {self:?}", "INTO_RESPONSE"); - (StatusCode::INTERNAL_SERVER_ERROR, "UNHANDLED_CLIENT_ERROR").into_response() + // Create a placeholder Axum response. + let mut response = StatusCode::INTERNAL_SERVER_ERROR.into_response(); + + // Insert the Error into the response. + response.extensions_mut().insert(self); + + response } } + +#[derive(Debug, strum_macros::AsRefStr)] +#[allow(non_camel_case_types)] +pub enum ClientError { + LOGIN_FAIL, + NO_AUTH, + INVALID_PARAMS, + SERVICE_ERROR, +} diff --git a/src/main.rs b/src/main.rs index 05b5727..b8eb052 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,8 +13,10 @@ use axum::routing::{get, get_service}; use axum::{middleware, Json, Router}; use axum::Server; use serde::Deserialize; +use serde_json::json; use tower_http::services::ServeDir; use tower_cookies::CookieManagerLayer; +use uuid::Uuid; mod ctx; mod error; @@ -56,10 +58,35 @@ async fn main() -> Result<()>{ /// /// * `res`: the response to map async fn main_response_mapper(res: Response) -> Response { - println!("->> {:<12} - main_response_mapper", "HANDLER"); + println!("->> {:<12} - main_response_mapper", "RES_MAPPER"); + let uuid = Uuid::new_v4(); + + // -- Get the eventual response error. + let service_error = res.extensions().get::(); + let client_status_error = service_error.map(|se| se.client_status_and_error()); + + // -- If client error, build the new response + let error_response = client_status_error + .as_ref() + .map(|(status_code, client_error)| { + let client_error_body = json!({ + "error": { + "type": client_error.as_ref(), + "req_uuid": uuid.to_string(), + } + }); + + println!(" ->> client_error_body: {client_error_body}"); + + // Build the new response from the client error body. + (*status_code, Json(client_error_body)).into_response() + }); + + // -- TODO: Build and log the server log line. + println!(" ->> server log line - {uuid} - Error: {service_error:?}"); println!(); - res + error_response.unwrap_or(res) } /// Serve static files