blob: 17e65d0a82b5b4acee03cf097e098f8a6a82219a [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! MCTP error types
//!
//! This module defines error types for MCTP operations, providing both
//! transport-level errors and higher-level service response codes.
use core::fmt;
/// Response codes from the MCTP service.
///
/// These codes indicate the result of an MCTP operation and are designed
/// to be compatible with IPC/RPC response handling.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ResponseCode {
/// Operation completed successfully.
Success = 0,
/// Internal server error.
InternalError = 1,
/// No space available (e.g., message buffers full).
NoSpace = 2,
/// Address/handle already in use.
AddrInUse = 3,
/// Operation timed out.
TimedOut = 4,
/// Invalid argument provided.
BadArgument = 5,
/// Server restarted, state lost.
ServerRestarted = 6,
}
impl ResponseCode {
/// Returns `true` if this represents a successful operation.
#[inline]
pub const fn is_success(self) -> bool {
matches!(self, ResponseCode::Success)
}
/// Returns `true` if this represents an error condition.
#[inline]
pub const fn is_error(self) -> bool {
!self.is_success()
}
/// Converts from a raw u8 value.
pub const fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ResponseCode::Success),
1 => Some(ResponseCode::InternalError),
2 => Some(ResponseCode::NoSpace),
3 => Some(ResponseCode::AddrInUse),
4 => Some(ResponseCode::TimedOut),
5 => Some(ResponseCode::BadArgument),
6 => Some(ResponseCode::ServerRestarted),
_ => None,
}
}
}
impl fmt::Display for ResponseCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ResponseCode::Success => write!(f, "success"),
ResponseCode::InternalError => write!(f, "internal error"),
ResponseCode::NoSpace => write!(f, "no space"),
ResponseCode::AddrInUse => write!(f, "address in use"),
ResponseCode::TimedOut => write!(f, "timed out"),
ResponseCode::BadArgument => write!(f, "bad argument"),
ResponseCode::ServerRestarted => write!(f, "server restarted"),
}
}
}
/// MCTP operation error.
///
/// This is the main error type returned by MCTP client operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MctpError {
/// The response code from the service.
pub code: ResponseCode,
}
impl MctpError {
/// Creates a new error from a response code.
pub const fn from_code(code: ResponseCode) -> Self {
MctpError { code }
}
/// Returns `true` if this is a timeout error.
#[inline]
pub const fn is_timeout(&self) -> bool {
matches!(self.code, ResponseCode::TimedOut)
}
}
impl fmt::Display for MctpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MCTP error: {}", self.code)
}
}
impl From<ResponseCode> for MctpError {
fn from(code: ResponseCode) -> Self {
MctpError::from_code(code)
}
}
#[cfg(test)]
mod tests {
use super::*;
extern crate std;
use std::string::ToString;
#[test]
fn response_code_is_success() {
assert!(ResponseCode::Success.is_success());
assert!(!ResponseCode::Success.is_error());
for &code in &[
ResponseCode::InternalError,
ResponseCode::NoSpace,
ResponseCode::AddrInUse,
ResponseCode::TimedOut,
ResponseCode::BadArgument,
ResponseCode::ServerRestarted,
] {
assert!(!code.is_success(), "{code:?} should not be success");
assert!(code.is_error(), "{code:?} should be an error");
}
}
#[test]
fn response_code_from_u8_roundtrip() {
for val in 0u8..=6 {
let code = ResponseCode::from_u8(val).expect("known code");
assert_eq!(code as u8, val);
}
assert_eq!(ResponseCode::from_u8(7), None);
assert_eq!(ResponseCode::from_u8(255), None);
}
#[test]
fn response_code_display() {
assert_eq!(ResponseCode::Success.to_string(), "success");
assert_eq!(ResponseCode::InternalError.to_string(), "internal error");
assert_eq!(ResponseCode::NoSpace.to_string(), "no space");
assert_eq!(ResponseCode::AddrInUse.to_string(), "address in use");
assert_eq!(ResponseCode::TimedOut.to_string(), "timed out");
assert_eq!(ResponseCode::BadArgument.to_string(), "bad argument");
assert_eq!(
ResponseCode::ServerRestarted.to_string(),
"server restarted"
);
}
#[test]
fn mctp_error_is_timeout() {
let e = MctpError::from_code(ResponseCode::TimedOut);
assert!(e.is_timeout());
let e2 = MctpError::from_code(ResponseCode::NoSpace);
assert!(!e2.is_timeout());
}
#[test]
fn mctp_error_display() {
let e = MctpError::from_code(ResponseCode::BadArgument);
assert_eq!(e.to_string(), "MCTP error: bad argument");
}
#[test]
fn mctp_error_from_response_code() {
let e: MctpError = ResponseCode::ServerRestarted.into();
assert_eq!(e.code, ResponseCode::ServerRestarted);
}
}