use static_assertions as sa;
sa::const_assert_eq!(std::mem::size_of::<libc::can_frame>(), 16);
#[cfg(feature = "socketcan")]
sa::assert_eq_size!(libc::can_frame, socketcan::CANFrame);
sa::assert_eq_size!(libc::can_frame, Frame);
sa::assert_eq_align!(libc::can_frame, Frame);
sa::assert_eq_size!(libc::can_frame, CanFrameWrapper);
sa::assert_eq_align!(libc::can_frame, CanFrameWrapper);
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone)]
pub struct Frame(
#[cfg_attr(feature = "serde", serde(with = "CanFrameWrapper"))]
libc::can_frame,
);
impl Frame {
const ID_MASK: u32 = 0x7FF;
const DATA_LEN: usize = 8;
const fn validate(self) -> Result<Self, BadLen> {
if self.0.can_dlc <= Self::DATA_LEN as u8 {
Ok(self)
} else {
Err(BadLen)
}
}
#[inline(always)] pub const fn from_libc_can_frame(
frame: libc::can_frame,
) -> Result<Self, BadLen> {
Self(frame).validate()
}
#[inline(always)] pub const fn into_libc_can_frame(self) -> libc::can_frame {
self.0
}
pub const fn from_id_data_len(
id_flags: u32,
data: [u8; 8],
len: u8,
) -> Result<Self, BadLen> {
let mut inner: libc::can_frame = unsafe {
std::mem::transmute([0u8; std::mem::size_of::<libc::can_frame>()])
};
inner.can_id = id_flags;
inner.can_dlc = len;
inner.data = data;
Self(inner).validate()
}
pub const fn from_id_slice(
id_flags: u32,
slice: &[u8],
) -> Result<Self, BadLen> {
if slice.len() > 8 {
return Err(BadLen);
}
let len: u8 = slice.len() as u8;
let mut data = [0u8; 8];
let mut i = 0;
while i < slice.len() {
data[i] = slice[i];
i += 1
}
Self::from_id_data_len(id_flags, data, len)
}
#[inline(always)] #[cfg(feature = "socketcan")]
pub fn from_socketcan(frame: socketcan::CANFrame) -> Result<Self, BadLen> {
Self::from_id_slice(frame.id(), frame.data())
}
#[inline(always)] #[cfg(feature = "socketcan")]
pub fn into_socketcan(
self,
) -> Result<socketcan::CANFrame, socketcan::ConstructionError> {
socketcan::CANFrame::new(self.id(), self.data(), false, false)
}
#[inline(always)] pub const fn id(&self) -> u32 {
self.0.can_id & Self::ID_MASK
}
#[inline(always)] pub const fn data_len(&self) -> usize {
self.0.can_dlc as usize
}
#[inline(always)] pub const fn data(&self) -> &[u8] {
debug_assert!(
self.data_len() <= Self::DATA_LEN,
"Class invariant 1 violated. Len is > Self::DATA_LEN"
);
unsafe {
core::slice::from_raw_parts(
&self.0.data as *const u8,
self.data_len(),
)
}
}
}
impl core::hash::Hash for Frame {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.can_id.hash(state);
self.0.can_dlc.hash(state);
self.data().hash(state);
}
}
impl PartialEq for Frame {
fn eq(&self, other: &Self) -> bool {
self.0.can_id == other.0.can_id
&& self.0.can_dlc == other.0.can_dlc
&& self.data() == other.data()
}
}
#[cfg(feature = "embedded-can")]
impl embedded_can::Frame for Frame {
fn new(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> {
let id: embedded_can::Id = id.into();
match id {
embedded_can::Id::Standard(id) => {
Frame::from_id_slice(id.as_raw().into(), data).ok()
}
embedded_can::Id::Extended(_) => None,
}
}
#[inline(always)] fn new_remote(_: impl Into<embedded_can::Id>, __: usize) -> Option<Self> {
None
}
#[inline(always)] fn is_extended(&self) -> bool {
false
}
#[inline(always)] fn is_remote_frame(&self) -> bool {
false
}
fn id(&self) -> embedded_can::Id {
embedded_can::Id::Standard(
embedded_can::StandardId::new(self.id().try_into().unwrap())
.unwrap(),
)
}
fn dlc(&self) -> usize {
self.0.can_dlc.into()
}
fn data(&self) -> &[u8] {
self.data()
}
}
impl std::fmt::Debug for Frame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(CanFrame))
.field("can_id", &self.0.can_id)
.field("can_dlc", &self.0.can_dlc)
.field("data", &self.0.data)
.finish()
}
}
impl std::fmt::Display for Frame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:3X}#{:X?}", self.id(), self.data())
}
}
#[cfg(feature = "socketcan")]
impl TryFrom<socketcan::CANFrame> for Frame {
type Error = BadLen;
#[inline(always)] fn try_from(frame: socketcan::CANFrame) -> Result<Self, Self::Error> {
Frame::from_socketcan(frame)
}
}
#[inline(always)] #[cfg(feature = "serde")]
const fn _zero(_frame: &libc::can_frame) -> u8 {
0
}
#[cfg(feature = "serde")]
#[inline(always)] fn deserialize_len<'de, D, const MAX: u8>(d: D) -> Result<u8, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::Deserialize;
let len = u8::deserialize(d)?;
if len <= MAX {
Ok(len)
} else {
Err(serde::de::Error::custom(BadLen))
}
}
#[cfg(feature = "serde")]
#[inline(always)] fn deserialize_len_8<'de, D>(d: D) -> Result<u8, D::Error>
where
D: serde::de::Deserializer<'de>,
{
deserialize_len::<'de, D, 8>(d)
}
#[repr(C, align(8))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(remote = "libc::can_frame"))]
struct CanFrameWrapper {
can_id: u32,
#[cfg_attr(
feature = "serde",
serde(deserialize_with = "deserialize_len_8")
)]
can_dlc: u8,
#[cfg_attr(feature = "serde", serde(skip, getter = "_zero"))]
__pad: u8,
#[cfg_attr(feature = "serde", serde(skip, getter = "_zero"))]
__res0: u8,
#[cfg_attr(feature = "serde", serde(skip, getter = "_zero"))]
__res1: u8,
data: [u8; 8],
}
impl CanFrameWrapper {
#[inline(always)] pub const fn into_libc_can_frame(self) -> libc::can_frame {
unsafe { std::mem::transmute(self) }
}
}
impl From<CanFrameWrapper> for libc::can_frame {
#[inline(always)] fn from(frame: CanFrameWrapper) -> Self {
frame.into_libc_can_frame()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(derive_more::Display, Debug, derive_more::Error)]
#[display = "Len (`can_dlc`) was > 8"]
pub struct BadLen;
#[cfg(test)]
mod tests {
use super::Frame;
#[test]
fn test_from_libc() {
let mut libc_frame: libc::can_frame = unsafe { std::mem::zeroed() };
libc_frame.can_id = 1;
libc_frame.can_dlc = 3;
libc_frame.data = [2, 3, 4, 5, 6, 7, 8, 9];
let frame = Frame::from_libc_can_frame(libc_frame.clone()).unwrap();
assert_eq!(frame.id(), libc_frame.can_id);
assert_eq!(
frame.data(),
&libc_frame.data[0..libc_frame.can_dlc as usize]
)
}
#[test]
#[cfg(feature = "socketcan")]
fn test_from_socketcan() {
let sc_frame =
socketcan::CANFrame::new(1, &[2, 3, 4], false, false).unwrap();
let frame = Frame::from_socketcan(sc_frame.clone()).unwrap();
assert_eq!(frame.id(), sc_frame.id());
assert_eq!(frame.data(), sc_frame.data());
}
#[test]
fn test_data() {
let frame =
Frame::from_id_data_len(1, [2, 3, 4, 5, 6, 7, 8, 9], 2).unwrap();
assert_eq!(frame.data(), &[2, 3]);
}
#[test]
fn test_validate_len() {
let ret = Frame::from_id_data_len(1, [2, 3, 4, 5, 6, 7, 8, 9], 255);
assert!(ret.is_err())
}
#[test]
#[cfg(feature = "serde")]
fn test_frame_serde_json() {
let expected =
Frame::from_id_data_len(1, [2, 3, 4, 5, 6, 7, 8, 9], 8).unwrap();
let json = serde_json::to_string(&expected).unwrap();
assert_eq!(
&json,
"{\"can_id\":1,\"can_dlc\":8,\"data\":[2,3,4,5,6,7,8,9]}"
);
let actual: Frame = serde_json::from_str(&json).unwrap();
assert_eq!(actual, expected);
const BAD_LEN: &str =
"{\"can_id\":1,\"can_dlc\":9,\"data\":[2,3,4,5,6,7,8,9]}";
let err = serde_json::from_str::<Frame>(BAD_LEN).unwrap_err();
assert_eq!(err.to_string(), "BadLen at line 1 column 23");
const BAD_DATA: &str =
"{\"can_id\":1,\"can_dlc\":8,\"data\":[2,3,4,5,6,7,8,9,10]}";
let err = serde_json::from_str::<Frame>(BAD_DATA).unwrap_err();
assert_eq!(err.to_string(), "trailing characters at line 1 column 49");
}
}