error_info/
lib.rs

1use std::collections::HashMap;
2
3use http::StatusCode;
4
5// Re-export derive macro crate
6#[allow(unused_imports)]
7#[macro_use]
8extern crate error_info_macros;
9#[doc(hidden)]
10pub use error_info_macros::*;
11
12/// Trait containing common information for an error
13pub trait ErrorInfo {
14    /// Retrieves the status of the error message
15    fn status(&self) -> StatusCode;
16    /// Retrieves the code of the error message
17    fn code(&self) -> &'static str;
18    /// Retrieves the raw message of the error, without variable replacements
19    fn raw_message(&self) -> &'static str;
20    /// Retrieves the error fields used on the message
21    fn fields(&self) -> HashMap<String, String>;
22    /// Builds the final error message with the variable fields interpolated
23    fn message(&self) -> String {
24        let mut message = self.raw_message().to_string();
25        for (k, v) in self.fields() {
26            message = message.replace(&format!("{{{k}}}"), &v);
27        }
28        message
29    }
30}
31
32#[cfg(feature = "summary")]
33#[derive(Debug, serde::Serialize)]
34/// Summary of an [ErrorInfo]
35pub struct ErrorInfoSummary {
36    /// HTTP status code
37    #[serde(serialize_with = "serialize_status")]
38    pub status: StatusCode,
39    /// Error code
40    pub code: &'static str,
41    /// Raw message, it might contain fields to be substituted.
42    ///
43    /// For example: `Missing field {field}`
44    pub raw_message: &'static str,
45}
46
47#[cfg(feature = "summary")]
48/// Array to collect all linked error info summaries, it shouldn't be used directly. Instead use `error_info::summary`
49#[linkme::distributed_slice]
50pub static ERROR_INFO_SUMMARY: [fn() -> Vec<ErrorInfoSummary>] = [..];
51
52#[cfg(feature = "summary")]
53/// Retrieves a summary of every [ErrorInfo] declared, sorted by status and code.
54///
55/// This could be exported to provide the base i18n file for errors.
56pub fn summary() -> Vec<ErrorInfoSummary> {
57    let mut ret = Vec::with_capacity(ERROR_INFO_SUMMARY.len());
58    for summaries_fn in ERROR_INFO_SUMMARY {
59        let mut summaries = summaries_fn();
60        ret.append(&mut summaries);
61    }
62    ret.sort_by_key(|s| (s.status, s.code));
63    ret
64}
65
66#[cfg(feature = "summary")]
67fn serialize_status<S>(status: &StatusCode, serializer: S) -> Result<S::Ok, S::Error>
68where
69    S: serde::Serializer,
70{
71    serializer.serialize_u16(status.as_u16())
72}