tripley-rpc-runtime-errors 0.1.2

Error types and runtime error codes for Tripley RPC.
Documentation
use serde::{Deserialize, Serialize};
use thiserror::Error;

pub type RpcResult<T> = Result<T, RuntimeError>;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(i32)]
pub enum RuntimeErrorCode {
    UnknownMessageKind = 1001,
    UnsupportedProtocolVersion = 1002,
    InvalidEnvelope = 1003,
    InvalidRequestId = 1004,
    InvalidInstanceId = 1005,
    InstanceNotFound = 1006,
    MethodNotFound = 1007,
    NotificationNotFound = 1008,
    PayloadDecodeFailed = 1009,
    PayloadEncodeFailed = 1010,
    ServiceActivationNotSupported = 1011,
    ServiceGuidNotFound = 1012,
    InstanceReleaseNotAllowed = 1013,
    RequestTimeout = 1014,
    UnsupportedCapability = 1015,
    BusinessErrorDeclared = 1016,
    DuplicateRequestId = 1017,
    RequestCancelUnsupported = 1018,
    AccessDenied = 1019,
    InternalRuntimeError = 1020,
}

impl RuntimeErrorCode {
    pub const fn as_i32(self) -> i32 {
        self as i32
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum ErrorKind {
    Transport = 1,
    Protocol = 2,
    Runtime = 3,
    Business = 4,
    Timeout = 5,
    Cancelled = 6,
}

impl ErrorKind {
    pub const fn as_u8(self) -> u8 {
        self as u8
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
#[error("{kind:?} error {code:?}: {message}")]
pub struct RuntimeError {
    pub code: RuntimeErrorCode,
    pub kind: ErrorKind,
    pub message: String,
}

impl RuntimeError {
    pub fn new(code: RuntimeErrorCode, kind: ErrorKind, message: impl Into<String>) -> Self {
        Self {
            code,
            kind,
            message: message.into(),
        }
    }

    pub fn protocol(code: RuntimeErrorCode, message: impl Into<String>) -> Self {
        Self::new(code, ErrorKind::Protocol, message)
    }

    pub fn transport(code: RuntimeErrorCode, message: impl Into<String>) -> Self {
        Self::new(code, ErrorKind::Transport, message)
    }

    pub fn runtime(code: RuntimeErrorCode, message: impl Into<String>) -> Self {
        Self::new(code, ErrorKind::Runtime, message)
    }
}

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

    #[test]
    fn runtime_error_code_values_are_stable() {
        assert_eq!(RuntimeErrorCode::UnknownMessageKind.as_i32(), 1001);
        assert_eq!(RuntimeErrorCode::UnsupportedProtocolVersion.as_i32(), 1002);
        assert_eq!(RuntimeErrorCode::InvalidEnvelope.as_i32(), 1003);
        assert_eq!(RuntimeErrorCode::InvalidRequestId.as_i32(), 1004);
        assert_eq!(RuntimeErrorCode::InvalidInstanceId.as_i32(), 1005);
        assert_eq!(RuntimeErrorCode::InstanceNotFound.as_i32(), 1006);
        assert_eq!(RuntimeErrorCode::MethodNotFound.as_i32(), 1007);
        assert_eq!(RuntimeErrorCode::NotificationNotFound.as_i32(), 1008);
        assert_eq!(RuntimeErrorCode::PayloadDecodeFailed.as_i32(), 1009);
        assert_eq!(RuntimeErrorCode::PayloadEncodeFailed.as_i32(), 1010);
        assert_eq!(
            RuntimeErrorCode::ServiceActivationNotSupported.as_i32(),
            1011
        );
        assert_eq!(RuntimeErrorCode::ServiceGuidNotFound.as_i32(), 1012);
        assert_eq!(RuntimeErrorCode::InstanceReleaseNotAllowed.as_i32(), 1013);
        assert_eq!(RuntimeErrorCode::RequestTimeout.as_i32(), 1014);
        assert_eq!(RuntimeErrorCode::UnsupportedCapability.as_i32(), 1015);
        assert_eq!(RuntimeErrorCode::BusinessErrorDeclared.as_i32(), 1016);
        assert_eq!(RuntimeErrorCode::DuplicateRequestId.as_i32(), 1017);
        assert_eq!(RuntimeErrorCode::RequestCancelUnsupported.as_i32(), 1018);
        assert_eq!(RuntimeErrorCode::AccessDenied.as_i32(), 1019);
        assert_eq!(RuntimeErrorCode::InternalRuntimeError.as_i32(), 1020);
    }

    #[test]
    fn error_kind_values_are_stable() {
        assert_eq!(ErrorKind::Transport.as_u8(), 1);
        assert_eq!(ErrorKind::Protocol.as_u8(), 2);
        assert_eq!(ErrorKind::Runtime.as_u8(), 3);
        assert_eq!(ErrorKind::Business.as_u8(), 4);
        assert_eq!(ErrorKind::Timeout.as_u8(), 5);
        assert_eq!(ErrorKind::Cancelled.as_u8(), 6);
    }
}