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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! Error handling for this crate
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// A Result containing a SystemError with its accompanying source
pub type TypedResult<T> = Result<T, TypedError>;
/// A Result containing a SystemError with its accompanying error and time
/// window
// TODO: Consider merging these two types by making level an Option.
pub type LeveledResult<T> = Result<T, LeveledError>;

/// A low-level error issued by the operating system
///
/// This implementation is custom. Do not confuse it with the traditional unix
/// errnos.
// TODO: Why can't we just use traditional unix errnos? The anyhow messages should be
// concrete enough.
#[derive(Error, Debug, Serialize, Deserialize, Clone, Copy)]
pub enum SystemError {
    #[error("Configuration error")]
    Config,
    #[error("Module config error")]
    ModuleConfig,
    #[error("Partition config error")]
    PartitionConfig,
    #[error("Error during Partition initialization")]
    PartitionInit,
    #[error("Segmentation error occured")]
    Segmentation,
    #[error("Time duration was exceeded by periodic process")]
    TimeDurationExceeded,
    #[error("Application error raised in partition")]
    ApplicationError,
    #[error("Unrecoverable errors")]
    Panic,
    #[error("Floating point error occurred")]
    FloatingPoint,
    #[error("cgroup related error")]
    CGroup,
}

/// The time window in which the error has occurred
#[derive(Debug, Clone, Copy)]
pub enum ErrorLevel {
    /// Synchronous to Partition Time Window
    Partition,
    /// During Module Init Phase
    ModuleInit,
    /// Asynchronous to Partition Time Window
    ModuleRun,
}

/// Combination of a SystemError with an anyhow error
#[derive(Error, Debug)]
#[error("{err:?}: {source:?}")]
pub struct TypedError {
    err: SystemError,
    source: anyhow::Error,
}

impl TypedError {
    /// Creates a new TypedError
    pub fn new(err: SystemError, source: anyhow::Error) -> Self {
        Self { err, source }
    }
    /// Returns the SystemError of this TypedError
    pub fn err(&self) -> SystemError {
        self.err
    }
    /// Returns the anyhow error of this TypedError
    pub fn source(&self) -> &anyhow::Error {
        &self.source
    }
}

/// Combination of a SystemError with an anyhow error and its time window
// TODO: Consider naming "level" "source" instead, as it indicates in which
// time window the error has occurred?
#[derive(Error, Debug)]
#[error("{err:?}: {level:?}, {source:?}")]
pub struct LeveledError {
    err: SystemError,
    level: ErrorLevel,
    source: anyhow::Error,
}

impl LeveledError {
    /// Creates a new LeveledError
    pub fn new(err: SystemError, level: ErrorLevel, source: anyhow::Error) -> Self {
        Self { err, level, source }
    }
    /// Returns the SystemError of this TypedError
    pub fn err(&self) -> SystemError {
        self.err
    }
    /// Returns the ErrorLevel of this TypedError
    pub fn level(&self) -> ErrorLevel {
        self.level
    }
    /// Returns the anyhow error of this TypedError
    pub fn source(&self) -> &anyhow::Error {
        &self.source
    }
}
impl From<LeveledError> for TypedError {
    fn from(le: LeveledError) -> Self {
        // Basically just cut off the level field
        Self {
            err: le.err,
            source: le.source,
        }
    }
}

/// Converts a Result into one of our own Result types
pub trait ResultExt<T> {
    /// Converts a Result to a TypedResult
    fn typ(self, err: SystemError) -> TypedResult<T>;
    /// Converts a Result to a LeveledResult
    fn lev_typ(self, err: SystemError, level: ErrorLevel) -> LeveledResult<T>;
}

/// Converts a TypedResult to one of our own Result types
pub trait TypedResultExt<T> {
    /// Creates a LeveledResult from a TypedResult
    fn lev(self, level: ErrorLevel) -> LeveledResult<T>;
}

impl<T> TypedResultExt<T> for TypedResult<T> {
    fn lev(self, level: ErrorLevel) -> LeveledResult<T> {
        // This basically just creates a LeveledError with all fields tken even from
        // the TypedResult, except the level being added.
        self.map_err(|e| LeveledError {
            err: e.err,
            level,
            source: e.source,
        })
    }
}

impl<T, E: Into<anyhow::Error>> ResultExt<T> for Result<T, E> {
    fn typ(self, err: SystemError) -> TypedResult<T> {
        self.map_err(|e| TypedError {
            err,
            source: e.into(),
        })
    }

    fn lev_typ(self, err: SystemError, level: ErrorLevel) -> LeveledResult<T> {
        self.map_err(|e| LeveledError {
            err,
            level,
            source: e.into(),
        })
    }
}