use axum::http::StatusCode;
use lazy_static::lazy_static;
use std::collections::HashMap;
use thiserror::Error;
mod conf;
pub mod extras;
mod loader;
mod locale;
pub use locale::get_current_locale;
pub use locale::set_current_locale;
pub type StandardErrorMessages = HashMap<String, HashMap<String, String>>;
#[cfg(feature = "askama")]
pub use extras::htmlres::HtmlRes;
pub use extras::interpolate::Interpolate;
pub use extras::status::Status;
#[derive(Debug, Clone, Error)]
#[error("Error {err_code} with status {status_code}")]
pub struct StandardError {
pub err_code: String,
pub status_code: StatusCode,
values: HashMap<String, String>,
pub message: String,
pub html: Option<String>,
}
impl StandardError {
pub fn new(code: &str) -> Self {
StandardError {
err_code: code.to_string(),
status_code: StatusCode::INTERNAL_SERVER_ERROR,
values: HashMap::new(),
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(),
),
html: None,
}
}
}
lazy_static! {
pub static ref settings: conf::Settings = conf::Settings::new().expect("improperly configured");
pub static ref error_messages: StandardErrorMessages =
StandardError::load_error_messages().expect("error loading error messages");
}
#[cfg(test)]
mod tests {
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<i32, StandardError> {
a.parse()
.map_err(|_: ParseIntError| StandardError::new("ER-0004"))
}
let res = foo("a").await;
if let Err(e) = res {
assert_eq!(e.status_code, StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(e.message, "Should be an integer".to_string())
}
Ok(())
}
#[tokio::test]
async fn test_status_code() -> Result<(), StandardError> {
async fn foo(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|_: ParseIntError| {
StandardError::new("ER-0004").code(StatusCode::BAD_REQUEST)
})
}
let res = foo("a").await;
if let Err(e) = res {
assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
assert_eq!(e.message, "Should be an integer".to_string())
}
Ok(())
}
#[tokio::test]
async fn test_interpolate_err() -> Result<(), StandardError> {
async fn foo(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|e: ParseIntError| {
StandardError::new("ER-0005").interpolate_err(e.to_string())
})
}
let res = foo("a").await;
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()
)
}
Ok(())
}
#[tokio::test]
async fn test_interpolate_values() -> Result<(), StandardError> {
async fn foo(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|_: ParseIntError| {
let mut values: HashMap<String, String> = HashMap::new();
values.insert("fname".to_string(), "ashu".to_string());
values.insert("lname".to_string(), "pednekar".to_string());
StandardError::new("ER-0006").interpolate_values(values)
})
}
let res = foo("a").await;
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()
)
}
Ok(())
}
#[tokio::test]
async fn test_chain() -> Result<(), StandardError> {
async fn foo(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|e: ParseIntError| {
let mut values: HashMap<String, String> = HashMap::new();
values.insert("fname".to_string(), "ashu".to_string());
values.insert("lname".to_string(), "pednekar".to_string());
StandardError::new("ER-0007")
.code(StatusCode::IM_A_TEAPOT)
.interpolate_values(values)
.interpolate_err(e.to_string())
})
}
let res = foo("a").await;
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(())
}
}