use can_dbc_pest::{Pair, Rule};
use crate::parser::parse_uint;
use crate::DbcError;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MessageId {
Standard(u16),
Extended(u32),
}
impl MessageId {
#[must_use]
pub fn raw(self) -> u32 {
match self {
Self::Standard(id) => u32::from(id),
Self::Extended(id) => id | 1 << 31,
}
}
}
impl TryFrom<u64> for MessageId {
type Error = DbcError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
u32::try_from(value)
.map_err(|_| DbcError::MessageIdOutOfRange(value))?
.try_into()
}
}
impl TryFrom<u32> for MessageId {
type Error = DbcError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
const EXTENDED_ID_FLAG: u32 = 1 << 31;
Ok(if value & EXTENDED_ID_FLAG != 0 {
Self::Extended(value & 0x1FFF_FFFF)
} else {
#[allow(clippy::cast_possible_truncation)]
let v = value as u16;
Self::Standard(v)
})
}
}
impl TryFrom<Pair<'_, Rule>> for MessageId {
type Error = DbcError;
fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
Self::try_from(parse_uint(&value)?)
}
}
#[cfg(test)]
mod tests {
use can_dbc_pest::Rule;
use crate::test_helpers::*;
use crate::MessageId;
#[test]
#[expect(clippy::unreadable_literal)]
fn extended_message_id_raw() {
let id = MessageId::Extended(2);
assert_eq!(id.raw(), 2 | 1 << 31);
let id = MessageId::Extended(0x1FFF_FFFF);
assert_eq!(id.raw(), 0b10011111_11111111_11111111_11111111);
}
#[test]
fn standard_message_id_raw() {
let id = MessageId::Standard(2);
assert_eq!(id.raw(), 2);
}
#[test]
fn try_from_u32_standard() {
let id = MessageId::try_from(500u32).unwrap();
assert_eq!(id, MessageId::Standard(500));
}
#[test]
fn try_from_u32_extended() {
let id = MessageId::try_from(2u32 | (1 << 31)).unwrap();
assert_eq!(id, MessageId::Extended(2));
}
#[test]
fn try_from_u64_standard() {
let id = MessageId::try_from(500u64).unwrap();
assert_eq!(id, MessageId::Standard(500));
}
#[test]
fn try_from_u64_extended() {
let id = MessageId::try_from(2u64 | (1 << 31)).unwrap();
assert_eq!(id, MessageId::Extended(2));
}
#[test]
fn standard_message_id_test() {
let val = test_into::<MessageId>("2", Rule::message_id);
assert_eq!(val, MessageId::Standard(2));
}
#[test]
fn extended_low_message_id_test() {
let s = (2u32 | 1 << 31).to_string();
let val = test_into::<MessageId>(&s, Rule::message_id);
assert_eq!(val, MessageId::Extended(2));
}
#[test]
fn extended_message_id_test() {
let s = (0x1FFF_FFFF_u32 | 1 << 31).to_string();
let val = test_into::<MessageId>(&s, Rule::message_id);
assert_eq!(val, MessageId::Extended(0x1FFF_FFFF));
}
#[test]
fn extended_message_id_test_max_29bit() {
let s = u32::MAX.to_string();
let val = test_into::<MessageId>(&s, Rule::message_id);
assert_eq!(val, MessageId::Extended(0x1FFF_FFFF));
}
}