simple-someip 0.10.0

A lightweight SOME/IP serialization and communication library
Documentation
use super::Error;

///Return code contained in a SOME/IP header.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ReturnCode {
    /// No error (0x00).
    Ok,
    /// An unspecified error occurred (0x01).
    NotOk,
    /// The requested service is unknown (0x02).
    UnknownService,
    /// The requested method is unknown (0x03).
    UnknownMethod,
    /// The service is not ready (0x04).
    NotReady,
    /// The service is not reachable (0x05).
    NotReachable,
    /// A timeout occurred (0x06).
    Timeout,
    /// The protocol version is not supported (0x07).
    WrongProtocolVersion,
    /// The interface version is not supported (0x08).
    WrongInterfaceVersion,
    /// The message is malformed (0x09).
    MalformedMessage,
    /// The message type is wrong for the context (0x0A).
    WrongMessageType,
    /// E2E: repeated message detected (0x0B).
    E2ERepeated,
    /// E2E: wrong sequence counter (0x0C).
    E2EWrongSequence,
    /// E2E: unspecified E2E error (0x0D).
    E2E,
    /// E2E: protection not available (0x0E).
    E2ENotAvailable,
    /// E2E: no new data (0x0F).
    E2ENoNewData,
    /// A generic error in the range 0x10..=0x1F.
    GenericError(u8),
    /// An interface-specific error in the range 0x20..=0x5E.
    InterfaceError(u8),
}

impl ReturnCode {
    /// Returns the raw byte value of the return code.
    #[must_use]
    pub const fn as_u8(self) -> u8 {
        match self {
            ReturnCode::Ok => 0x00,
            ReturnCode::NotOk => 0x01,
            ReturnCode::UnknownService => 0x02,
            ReturnCode::UnknownMethod => 0x03,
            ReturnCode::NotReady => 0x04,
            ReturnCode::NotReachable => 0x05,
            ReturnCode::Timeout => 0x06,
            ReturnCode::WrongProtocolVersion => 0x07,
            ReturnCode::WrongInterfaceVersion => 0x08,
            ReturnCode::MalformedMessage => 0x09,
            ReturnCode::WrongMessageType => 0x0a,
            ReturnCode::E2ERepeated => 0x0b,
            ReturnCode::E2EWrongSequence => 0x0c,
            ReturnCode::E2E => 0x0d,
            ReturnCode::E2ENotAvailable => 0x0e,
            ReturnCode::E2ENoNewData => 0x0f,
            ReturnCode::GenericError(value) | ReturnCode::InterfaceError(value) => value,
        }
    }
}

impl TryFrom<u8> for ReturnCode {
    type Error = Error;
    fn try_from(value: u8) -> Result<Self, Error> {
        match value {
            0x00 => Ok(ReturnCode::Ok),
            0x01 => Ok(ReturnCode::NotOk),
            0x02 => Ok(ReturnCode::UnknownService),
            0x03 => Ok(ReturnCode::UnknownMethod),
            0x04 => Ok(ReturnCode::NotReady),
            0x05 => Ok(ReturnCode::NotReachable),
            0x06 => Ok(ReturnCode::Timeout),
            0x07 => Ok(ReturnCode::WrongProtocolVersion),
            0x08 => Ok(ReturnCode::WrongInterfaceVersion),
            0x09 => Ok(ReturnCode::MalformedMessage),
            0x0a => Ok(ReturnCode::WrongMessageType),
            0x0b => Ok(ReturnCode::E2ERepeated),
            0x0c => Ok(ReturnCode::E2EWrongSequence),
            0x0d => Ok(ReturnCode::E2E),
            0x0e => Ok(ReturnCode::E2ENotAvailable),
            0x0f => Ok(ReturnCode::E2ENoNewData),
            0x10..=0x1f => Ok(ReturnCode::GenericError(value)),
            0x20..=0x5e => Ok(ReturnCode::InterfaceError(value)),
            _ => Err(Error::InvalidReturnCode(value)),
        }
    }
}

impl From<ReturnCode> for u8 {
    fn from(return_code: ReturnCode) -> u8 {
        match return_code {
            ReturnCode::Ok => 0x00,
            ReturnCode::NotOk => 0x01,
            ReturnCode::UnknownService => 0x02,
            ReturnCode::UnknownMethod => 0x03,
            ReturnCode::NotReady => 0x04,
            ReturnCode::NotReachable => 0x05,
            ReturnCode::Timeout => 0x06,
            ReturnCode::WrongProtocolVersion => 0x07,
            ReturnCode::WrongInterfaceVersion => 0x08,
            ReturnCode::MalformedMessage => 0x09,
            ReturnCode::WrongMessageType => 0x0a,
            ReturnCode::E2ERepeated => 0x0b,
            ReturnCode::E2EWrongSequence => 0x0c,
            ReturnCode::E2E => 0x0d,
            ReturnCode::E2ENotAvailable => 0x0e,
            ReturnCode::E2ENoNewData => 0x0f,
            ReturnCode::GenericError(value) | ReturnCode::InterfaceError(value) => value,
        }
    }
}

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

    // Every valid u8 should round-trip: TryFrom then From gives back the same byte.
    // Invalid bytes (0x5f..=0xff) must return an error.
    #[test]
    fn all_valid_u8_values_round_trip() {
        for byte in 0x00u8..=0xffu8 {
            match ReturnCode::try_from(byte) {
                Ok(code) => {
                    assert!(
                        byte < 0x5f,
                        "0x{byte:02X} should be invalid but decoded successfully"
                    );
                    assert_eq!(u8::from(code), byte, "round-trip failed for 0x{byte:02X}");
                }
                Err(_) => {
                    assert!(
                        byte >= 0x5f,
                        "0x{byte:02X} should be valid but failed to decode"
                    );
                }
            }
        }
    }

    // Named variants
    #[test]
    fn named_variants_decode_correctly() {
        let cases = [
            (0x00, ReturnCode::Ok),
            (0x01, ReturnCode::NotOk),
            (0x02, ReturnCode::UnknownService),
            (0x03, ReturnCode::UnknownMethod),
            (0x04, ReturnCode::NotReady),
            (0x05, ReturnCode::NotReachable),
            (0x06, ReturnCode::Timeout),
            (0x07, ReturnCode::WrongProtocolVersion),
            (0x08, ReturnCode::WrongInterfaceVersion),
            (0x09, ReturnCode::MalformedMessage),
            (0x0a, ReturnCode::WrongMessageType),
            (0x0b, ReturnCode::E2ERepeated),
            (0x0c, ReturnCode::E2EWrongSequence),
            (0x0d, ReturnCode::E2E),
            (0x0e, ReturnCode::E2ENotAvailable),
            (0x0f, ReturnCode::E2ENoNewData),
        ];
        for (byte, expected) in cases {
            assert_eq!(ReturnCode::try_from(byte).unwrap(), expected);
        }
    }

    // GenericError range: 0x10..=0x1f
    #[test]
    fn generic_error_range_decodes_and_preserves_value() {
        for byte in 0x10u8..=0x1f {
            assert_eq!(
                ReturnCode::try_from(byte).unwrap(),
                ReturnCode::GenericError(byte)
            );
        }
    }

    // InterfaceError range: 0x20..=0x5e
    #[test]
    fn interface_error_range_decodes_and_preserves_value() {
        for byte in 0x20u8..=0x5e {
            assert_eq!(
                ReturnCode::try_from(byte).unwrap(),
                ReturnCode::InterfaceError(byte)
            );
        }
    }

    // Invalid values
    #[test]
    fn invalid_values_return_error() {
        for byte in [0x5f, 0x60, 0xff] {
            assert!(
                ReturnCode::try_from(byte).is_err(),
                "0x{byte:02X} should be invalid"
            );
        }
    }

    #[test]
    fn generic_error_as_u8_preserves_value() {
        for byte in 0x10u8..=0x1f {
            assert_eq!(ReturnCode::GenericError(byte).as_u8(), byte);
        }
    }

    #[test]
    fn interface_error_as_u8_preserves_value() {
        for byte in 0x20u8..=0x5e {
            assert_eq!(ReturnCode::InterfaceError(byte).as_u8(), byte);
        }
    }

    #[test]
    fn from_u8_for_generic_and_interface_errors() {
        assert_eq!(u8::from(ReturnCode::GenericError(0x15)), 0x15);
        assert_eq!(u8::from(ReturnCode::InterfaceError(0x30)), 0x30);
    }

    #[test]
    fn as_u8_all_named_variants() {
        assert_eq!(ReturnCode::Ok.as_u8(), 0x00);
        assert_eq!(ReturnCode::NotOk.as_u8(), 0x01);
        assert_eq!(ReturnCode::UnknownService.as_u8(), 0x02);
        assert_eq!(ReturnCode::UnknownMethod.as_u8(), 0x03);
        assert_eq!(ReturnCode::NotReady.as_u8(), 0x04);
        assert_eq!(ReturnCode::NotReachable.as_u8(), 0x05);
        assert_eq!(ReturnCode::Timeout.as_u8(), 0x06);
        assert_eq!(ReturnCode::WrongProtocolVersion.as_u8(), 0x07);
        assert_eq!(ReturnCode::WrongInterfaceVersion.as_u8(), 0x08);
        assert_eq!(ReturnCode::MalformedMessage.as_u8(), 0x09);
        assert_eq!(ReturnCode::WrongMessageType.as_u8(), 0x0a);
        assert_eq!(ReturnCode::E2ERepeated.as_u8(), 0x0b);
        assert_eq!(ReturnCode::E2EWrongSequence.as_u8(), 0x0c);
        assert_eq!(ReturnCode::E2E.as_u8(), 0x0d);
        assert_eq!(ReturnCode::E2ENotAvailable.as_u8(), 0x0e);
        assert_eq!(ReturnCode::E2ENoNewData.as_u8(), 0x0f);
    }
}