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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::any::Any;
use std::convert::Infallible;
use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;
use indexmap::IndexMap;

#[derive(Debug)]
pub struct Error {
    pub message: String,
    pub prefixes: Option<Vec<String>>,
    pub title: Option<String>,
    pub code: Option<u16>,
    pub errors: Option<IndexMap<String, String>>,
    pub platform_native_object: Option<Arc<dyn Any + Send + Sync>>,
}

impl Error {

    pub fn new(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
            prefixes: None,
            title: None,
            code: None,
            errors: None,
            platform_native_object: None,
        }
    }

    pub fn new_with_code(message: impl Into<String>, code: u16) -> Self {
        Self {
            message: message.into(),
            prefixes: None,
            title: None,
            code: Some(code),
            errors: None,
            platform_native_object: None,
        }
    }

    pub fn new_with_code_title(message: impl Into<String>, code: u16, title: impl Into<String>) -> Self {
        Self {
            message: message.into(),
            prefixes: None,
            title: Some(title.into()),
            code: Some(code),
            errors: None,
            platform_native_object: None,
        }
    }

    pub fn new_with_code_title_errors(message: impl Into<String>, code: u16, title: impl Into<String>, errors: IndexMap<String, String>) -> Self {
        Self {
            message: message.into(),
            prefixes: None,
            title: Some(title.into()),
            code: Some(code),
            errors: Some(errors),
            platform_native_object: None,
        }
    }

    pub fn new_with_title_errors(message: impl Into<String>, title: impl Into<String>, errors: IndexMap<String, String>) -> Self {
        Self {
            message: message.into(),
            prefixes: None,
            title: Some(title.into()),
            code: None,
            errors: Some(errors),
            platform_native_object: None,
        }
    }

    pub fn prefixed(&self, prefix: impl Into<String>) -> Self {
        Self {
            message: self.message.clone(),
            prefixes: {
                let mut original = self.prefixes.clone().unwrap_or(vec![]);
                original.insert(0, prefix.into());
                Some(original)
            },
            title: self.title.clone(),
            code: self.code.clone(),
            errors: self.errors.clone(),
            platform_native_object: self.platform_native_object.clone(),
        }
    }

    pub fn title(&self) -> &str {
        self.title.as_ref().map_or("InternalServerError", AsRef::as_ref)
    }

    pub fn message(&self) -> String {
        if let Some(prefixes) = &self.prefixes {
            let mut result = "".to_owned();
            for prefix in prefixes {
                result += prefix.as_str();
                result += ": ";
            }
            result += self.message.as_str();
            result
        } else {
            self.message.clone()
        }
    }

    pub fn assign_platform_native_object<T: 'static + Send + Sync>(&mut self, val: T) {
        self.platform_native_object = Some(Arc::new(val));
    }

    pub fn platform_native_object<T: 'static + Send>(&self) -> Option<&T> {
        self.platform_native_object.as_ref().map(|boxed| boxed.downcast_ref()).flatten()
    }
}

impl Display for Error {

    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.message().as_str())
    }
}

impl std::error::Error for Error { }

impl From<std::io::Error> for Error {

    fn from(value: std::io::Error) -> Self {
        Self::new(value.to_string())
    }
}

impl From<Infallible> for Error {
    fn from(_value: Infallible) -> Self {
        Self::new("infallible")
    }
}