1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::panic::Location;

#[derive(Debug)]
pub struct ServiceError {
    trace: ErrorTrace,
    kind: ServiceErrorKind,
    source: Option<Box<dyn Error + Send>>,
}

impl Display for ServiceError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match &self.kind {
            ServiceErrorKind::InternalError { code } => {
                write!(f, "internal error [error_code = `{code}`]")?;
            }
            ServiceErrorKind::DependencyError { dependency_name, code, } => {
                write!(f, "dependency error [dependency_name = `{dependency_name}`] [error_code = `{code}`]")?;
            }
        };

        Ok(())
    }
}

impl Error for ServiceError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.source {
            None => { None }
            Some(err) => { Some(&**err) }
        }
    }
}

impl ServiceError {
    pub fn internal_error(
        code: Cow<'static, str>,
        source: Option<Box<dyn Error + Send>>,
        trace: ErrorTrace,
    ) -> ServiceError {
        ServiceError {
            trace,
            source,
            kind: ServiceErrorKind::InternalError { code },
        }
    }

    pub fn dependency_error(
        dependency_name: Cow<'static, str>,
        code: Cow<'static, str>,
        source: Option<Box<dyn Error + Send>>,
        trace: ErrorTrace,
    ) -> ServiceError {
        ServiceError {
            trace,
            source,
            kind: ServiceErrorKind::DependencyError {
                code,
                dependency_name,
            },
        }
    }

    pub fn set_error<T>(&mut self, error: T)
        where T: Error + Send + 'static
    {
        self.source = Some(Box::new(error))
    }

    pub fn with_error<T>(mut self, error: T) -> ServiceError
        where T: Error + Send + 'static
    {
        self.set_error(error);
        self
    }

    pub fn trace(&self) -> &ErrorTrace {
        &self.trace
    }
}

#[derive(Debug)]
enum ServiceErrorKind {
    InternalError {
        code: Cow<'static, str>,
    },
    DependencyError {
        code: Cow<'static, str>,
        dependency_name: Cow<'static, str>,
    },
}

#[derive(Debug)]
pub enum ErrorTrace {
    // Backtrace(Backtrace),
    Location(&'static Location<'static>),
}


pub struct MultilineDisplayAdapter<E: Error>(pub E);

impl<E: Error> Display for MultilineDisplayAdapter<E> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "Error: {}", self.0)?;

        let mut cause = self.0.source();
        if self.0.source().is_some() {
            writeln!(f, "Caused by:")?;
        }

        while let Some(error) = cause {
            writeln!(f, "   {}", error)?;
            cause = error.source();
        }

        Ok(())
    }
}