logforth_core/
error.rs

1// Copyright 2024 FastLabs Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16use std::io;
17
18/// The error struct of logforth.
19pub struct Error {
20    message: String,
21    source: Option<anyhow::Error>,
22    context: Vec<(&'static str, String)>,
23}
24
25impl fmt::Display for Error {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        write!(f, "{}", self.message)?;
28
29        if !self.context.is_empty() {
30            write!(f, ", context: {{ ")?;
31            write!(
32                f,
33                "{}",
34                self.context
35                    .iter()
36                    .map(|(k, v)| format!("{k}: {v}"))
37                    .collect::<Vec<_>>()
38                    .join(", ")
39            )?;
40            write!(f, " }}")?;
41        }
42
43        if let Some(source) = &self.source {
44            write!(f, ", source: {source}")?;
45        }
46
47        Ok(())
48    }
49}
50
51impl fmt::Debug for Error {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        // If alternate has been specified, we will print like Debug.
54        if f.alternate() {
55            let mut de = f.debug_struct("Error");
56            de.field("message", &self.message);
57            de.field("context", &self.context);
58            de.field("source", &self.source);
59            return de.finish();
60        }
61
62        write!(f, "{}", self.message)?;
63        writeln!(f)?;
64
65        if !self.context.is_empty() {
66            writeln!(f)?;
67            writeln!(f, "Context:")?;
68            for (k, v) in self.context.iter() {
69                writeln!(f, "   {k}: {v}")?;
70            }
71        }
72        if let Some(source) = &self.source {
73            writeln!(f)?;
74            writeln!(f, "Source:")?;
75            writeln!(f, "   {source:#}")?;
76        }
77
78        Ok(())
79    }
80}
81
82impl std::error::Error for Error {
83    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84        self.source.as_ref().map(|v| v.as_ref())
85    }
86}
87
88impl Error {
89    /// Create a new Error with error kind and message.
90    pub fn new(message: impl Into<String>) -> Self {
91        Self {
92            message: message.into(),
93            source: None,
94            context: vec![],
95        }
96    }
97
98    /// Add more context in error.
99    pub fn with_context(mut self, key: &'static str, value: impl ToString) -> Self {
100        self.context.push((key, value.to_string()));
101        self
102    }
103
104    /// Set source for error.
105    ///
106    /// # Notes
107    ///
108    /// If the source has been set, we will raise a panic here.
109    pub fn set_source(mut self, src: impl Into<anyhow::Error>) -> Self {
110        debug_assert!(self.source.is_none(), "the source error has been set");
111
112        self.source = Some(src.into());
113        self
114    }
115
116    /// Default constructor for [`Error`] from [`io::Error`].
117    pub fn from_io_error(err: io::Error) -> Error {
118        Error::new("failed to perform io").set_source(err)
119    }
120
121    /// Default constructor for [`Error`] from [`fmt::Error`].
122    pub fn from_fmt_error(err: fmt::Error) -> Error {
123        Error::new("failed to perform format").set_source(err)
124    }
125}