4 releases
0.1.5 | Sep 12, 2024 |
---|---|
0.1.4 | Sep 7, 2024 |
0.1.1 | Aug 17, 2024 |
#566 in HTTP server
288 downloads per month
32KB
497 lines
standard-error
standard-error
is a Rust crate designed to simplify the handling of error messages for various locales. It reads error messages from YAML and other sources, providing a structured and efficient way to manage errors in your applications.
Features
- Locale-Specific Error Messages: Automatically fetches error messages for the specified locale.
- Flexible Data Sources: Reads error messages from YAML files and other formats.
- Easy Integration: Designed to work seamlessly with Rust web frameworks like
Axum
.
Usage
Below is an example of how to use standard-error
in a web application using the Axum
framework:
use axum::{
extract::Query,
Json,
http::StatusCode,
};
use serde_json::{json, Value};
use std::collections::HashMap;
use standard_error::StandardError;
pub async fn my_handler(Query(params): Query<HashMap<String, String>>) -> Result<Json<Value>, StandardError> {
let group = match params.get("name") {
Some(g) => g,
None => {
return Err(StandardError::new("ER-0037").code(StatusCode::BAD_REQUEST));
}
};
Ok(Json(json!({"message": "success"})))
}
Explanation
- Error Handling: In the example, StandardError is used to handle cases where a required query parameter is missing. If the group parameter is not provided, the function returns a StandardError with the appropriate error code and HTTP status.
- Error Code: The error code "ER-0037" is used to look up the relevant error message based on the locale.
Scenarios
Basic Error Handling with Default Status Code
If a parsing error occurs (e.g., trying to convert a string to an integer), a StandardError can be returned with a default status code (500 INTERNAL_SERVER_ERROR
):
async fn parse_int(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|_| StandardError::new("ER-0004"))
}
// Example usage
let res = parse_int("abc").await;
// This will return an error with code "ER-0004" and status code 500.
Setting a Custom Status Code
You can customize the status code to something other than the default. For example, returning 400 BAD_REQUEST
:
async fn parse_int_custom(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|_| StandardError::new("ER-0004").code(StatusCode::BAD_REQUEST))
}
// Example usage
let res = parse_int_custom("abc").await;
// This will return an error with code "ER-0004" and status code 400.
Interpolating Error Details
StandardError
supports error message interpolation. For example, you can include the specific error details when returning the error:
async fn parse_with_error_interpolation(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|e| StandardError::new("ER-0005").interpolate_err(e.to_string()))
}
// Example usage
let res = parse_with_error_interpolation("abc").await;
// This will return an error with code "ER-0005" and message: "Should be an integer: invalid digit found in string".
Interpolating Values
You can interpolate additional values into the error message, such as user-specific data:
async fn parse_with_value_interpolation(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|_| {
let mut values = 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)
})
}
// Example usage
let res = parse_with_value_interpolation("abc").await;
// This will return an error with code "ER-0006" and message: "Should be an integer - fname: ashu | lname: pednekar".
Chaining Status Code and Interpolations
You can chain multiple methods on StandardError
, like setting a custom status code and interpolating both error details and values:
async fn parse_with_chained_errors(a: &str) -> Result<i32, StandardError> {
a.parse().map_err(|e| {
let mut values = 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())
})
}
// Example usage
let res = parse_with_chained_errors("abc").await;
// This will return an error with code "ER-0007", status code 418, and message:
// "Should be an integer - fname: ashu | lname: pednekar - invalid digit found in string".
Installation
Add standard-error to your Cargo.toml:
[dependencies]
standard-error = "0.1"
or with cargo
cargo add standard-error
Note: Add features
diesel
orgit
to auto-magically handle errors raised by these diesel and git2 crates respectively
Configuration
To configure standard-error, you can provide YAML files containing error messages for different locales. The crate will automatically load the correct message based on the locale specified in your application.
Example YAML structure:
errors:
- code: ER-0004
detail_en_US: "Should be an integer"
detail_hi_IN: "एक पूर्णांक होना चाहिए"
- code: ER-0005
detail_en_US: "Should be an integer: [err]"
detail_hi_IN: "एक पूर्णांक होना चाहिए: [err]"
- code: ER-0006
detail_en_US: "Should be an integer - fname: [fname] | lname: [lname]"
detail_hi_IN: "एक पूर्णांक होना चाहिए - fname: [fname] | lname: [lname]"
- code: ER-0007
detail_en_US: "Should be an integer - fname: [fname] | lname: [lname] - [err]"
detail_hi_IN: "एक पूर्णांक होना चाहिए - fname: [fname] | lname: [lname] - [err]"
Keep this yaml file (
errors.yaml
) at the root of your directory, outsidesrc
. Or you can keep it wherever you please and set theERROR_YAML_FILE_PATH
environment variable.
As for the locale configuration, by default, the crate picks up the default value from the
DEFAULT_LOCALE
env, which is set toen_US
by default.
- You can change this env to any value you like, provided the corresponding keys are present in yout errors yaml file.
If you wish to dynamically change the locale programmatically at any given point, you can call the
standard_error::set_current_locale
function like so
use standard_error::set_current_locale;
fn my_business_logic(){
//...
set_current_locale("hi_IN");
//...
}
This sets a thread local refcell variable that'll persist throught the thread. Since it's a
RefCell
value, and not something likeArc
, you don't have to worry about it leaking into your other threads/requests.
Dependencies
~8–23MB
~356K SLoC