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
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};

use bytecheck::CheckBytes;
use rkyv::{Archive, Deserialize, Serialize};

#[repr(C)]
#[derive(Serialize, Deserialize, Archive, PartialEq, Eq)]
#[archive(compare(PartialEq))]
#[archive_attr(derive(CheckBytes, PartialEq, Eq, Debug))]
/// Status information around the cause of a message request failing.
///
/// This includes a generic status code and message.
pub struct Status {
    /// The generic error code of the request.
    pub code: ErrorCode,
    /// The display message for the error.
    pub message: String,
}

impl Status {
    /// The server is running but the specified service does not exist
    /// or cannot handle messages at this time.
    pub fn unavailable(msg: impl Display) -> Self {
        Self {
            code: ErrorCode::ServiceUnavailable,
            message: msg.to_string(),
        }
    }

    /// An internal error occurred while processing the message.
    pub fn internal(msg: impl Display) -> Self {
        Self {
            code: ErrorCode::InternalError,
            message: msg.to_string(),
        }
    }

    /// The provided message data is invalid or unable to be deserialized
    /// by the server processing it.
    pub fn invalid() -> Self {
        Self {
            code: ErrorCode::InvalidPayload,
            message: "Invalid message payload was provided to be deserialized."
                .to_string(),
        }
    }

    /// The connection is closed or interrupted during the operation.
    pub fn connection(msg: impl Display) -> Self {
        Self {
            code: ErrorCode::ConnectionError,
            message: msg.to_string(),
        }
    }

    /// The RPC channel was closed before the message could be completed.
    pub fn closed() -> Self {
        Self {
            code: ErrorCode::ConnectionError,
            message: "The connection was closed before the message could be received."
                .to_string(),
        }
    }
}

impl Display for Status {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}: {}", self.code, self.message)
    }
}

impl Debug for Status {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Status")
            .field("code", &self.code)
            .field("message", &self.message)
            .finish()
    }
}

impl Error for Status {}

#[repr(C)]
#[derive(Serialize, Deserialize, Archive, PartialEq, Eq, Debug)]
#[archive(compare(PartialEq))]
#[archive_attr(derive(CheckBytes, Debug, PartialEq, Eq))]
/// A generic error code describing the high level reason why the request failed.
pub enum ErrorCode {
    /// The server is running but the specified service does not exist
    /// or cannot handle messages at this time.
    ServiceUnavailable,
    /// An internal error occurred while processing the message.
    InternalError,
    /// The provided message data is invalid or unable to be deserialized
    /// by the server processing it.
    InvalidPayload,
    /// The connection is closed or interrupted during the operation.
    ConnectionError,
    /// The RPC channel was closed before the message could be completed.
    ChannelClosed,
}

#[cfg(test)]
mod tests {
    use super::*;

    fn test_status_variant(status: Status) {
        println!("Testing: {:?}", &status);
        let bytes = rkyv::to_bytes::<_, 1024>(&status).expect("Serialize OK");
        let archived =
            rkyv::check_archived_root::<'_, Status>(&bytes).expect("Archive OK");
        assert_eq!(
            archived, &status,
            "Archived value and original value should match"
        );
        let copy: Status = rkyv::from_bytes(&bytes).expect("Deserialize OK");
        assert_eq!(
            copy, status,
            "Deserialized value and original value should match"
        );
    }

    #[test]
    fn test_variants() {
        test_status_variant(Status::invalid());
        test_status_variant(Status::closed());
        test_status_variant(Status::connection("Test connection failed."));
        test_status_variant(Status::unavailable("Test unavailable."));
        test_status_variant(Status::internal("Test internal error."));
    }
}