Skip to content

Commit

Permalink
Merge pull request #12 from de-vri-es/can-id-macros
Browse files Browse the repository at this point in the history
Add macros for compile time checked CAN ID construction.
  • Loading branch information
de-vri-es authored Oct 23, 2024
2 parents 8145584 + ddabb0c commit 1ef288c
Show file tree
Hide file tree
Showing 27 changed files with 536 additions and 2 deletions.
128 changes: 126 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ members = [
"can-socket",
"canopen-tokio",
]

[patch.crates-io]
trybuild = { git = "https://github.com/de-vri-es/trybuild", branch = "full-build" }
3 changes: 3 additions & 0 deletions can-socket/CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Unreleased
- [add][minor] Add `id!()`, `base_id!()` and `extended_id!()` macros to construct compile time checked CAN IDs.

# Version 0.1.6 - 2024-10-23
- [add][minor] Add `get_loopback()`/`set_loopback()`.
- [add][minor] Add `get_receive_own_messages()`/`set_receive_own_messages()`.
Expand Down
1 change: 1 addition & 0 deletions can-socket/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ can-socket = { path = ".", features = ["tokio"] }
clap = { version = "4.4.4", features = ["derive"] }
rand = "0.8.5"
tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"] }
trybuild = { version = "1.0.101", features = ["diff"] }
131 changes: 131 additions & 0 deletions can-socket/src/can_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,115 @@ use crate::error::{InvalidId, ParseIdError};
pub const MAX_CAN_ID_BASE: u16 = 0x7FF;
pub const MAX_CAN_ID_EXTENDED: u32 = 0x1FFF_FFFF;

/// Construct a [`CanId`] (base or extended) that is checked at compile time.
///
/// You can use any expression that can be evaluated at compile time and results in a `u32`.
///
/// By default, if the value fits in a base CAN ID, a base CAN ID is created.
/// You can also explicitly ask for a base or extended ID.
///
/// Usage:
/// ```
/// # use can_socket::{CanId, id};
/// let id: CanId = id!(0x100 | 0x005);
/// assert2::let_assert!(CanId::Base(id) = id);
/// assert2::assert!(id.as_u16() == 0x105);
/// ```
///
/// Force construction of a `CanId::Base` (does not compile because the ID only fits as an extended ID):
/// ```compile_fail
/// # use can_socket::{CanId, id};
/// let id: CanId = id!(base: 0x10 << 16 | 0x50);
/// ```
///
/// Force construction of a `CanId::Extended`:
/// ```
/// # use can_socket::{CanId, id};
/// let id: CanId = id!(extended: 0x100 | 0x005);
/// assert2::let_assert!(CanId::Extended(id) = id);
/// assert2::assert!(id.as_u32() == 0x105);
/// ```
#[macro_export]
macro_rules! id {
($n:expr) => {
{
#[allow(clippy::all)]
const { ::core::assert!(($n) <= $crate::MAX_CAN_ID_EXTENDED, "invalid CAN ID") };
unsafe {
if ($n) as u32 <= $crate::MAX_CAN_ID_BASE as u32 {
$crate::CanId::Base($crate::CanBaseId::new_unchecked(($n) as u16))
} else {
$crate::CanId::Extended($crate::CanExtendedId::new_unchecked($n))
}
}
}
};
(base: $n:expr) => {
$crate::CanId::Base($crate::base_id!($n))
};
(extended: $n:expr) => {
$crate::CanId::Extended($crate::extended_id!($n))
};
}

/// Construct a [`CanBaseId`] that is checked at compile time.
///
/// You can use any expression that can be evaluated at compile time and results in a `u16`.
///
/// Usage:
/// ```
/// # use assert2::assert;
/// # use can_socket::{CanBaseId, base_id};
/// let id: CanBaseId = base_id!(0x100 | 0x005);
/// assert!(id.as_u16() == 0x105);
/// ```
///
/// Will not accept invalid IDs:
/// ```compile_fail
/// # use can_socket::{CanBaseId, base_id};
/// let id: CanBaseId = base_id!(0x800);
/// ```
#[macro_export]
macro_rules! base_id {
($n:expr) => {
{
#[allow(clippy::all)]
const { ::core::assert!(($n) <= $crate::MAX_CAN_ID_BASE, "invalid base CAN ID") };
unsafe {
$crate::CanBaseId::new_unchecked($n)
}
}
};
}

/// Construct a [`CanExtendedId`] that is checked at compile time.
///
/// You can use any expression that can be evaluated at compile time and results in a `u32`.
///
/// Usage:
/// ```
/// # use assert2::assert;
/// # use can_socket::{CanExtendedId, extended_id};
/// let id: CanExtendedId = extended_id!(0x10 << 16 | 0x50);
/// assert!(id.as_u32() == 0x10_0050);
/// ```
///
/// Will not accept invalid IDs:
/// ```compile_fail
/// # use can_socket::{CanBaseId, extended_id};
/// let id: CanExtendedId = extended_id!(0x2000_0000);
/// ```
#[macro_export]
macro_rules! extended_id {
($n:expr) => {
unsafe {
#[allow(clippy::all)]
const { ::core::assert!(($n) <= $crate::MAX_CAN_ID_EXTENDED, "invalid extended CAN ID"); };
$crate::CanExtendedId::new_unchecked($n)
}
};
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[repr(C)]
pub enum CanId {
Expand Down Expand Up @@ -110,6 +219,17 @@ impl CanBaseId {
pub const fn as_u16(self) -> u16 {
self.id
}

/// Create a new base CAN ID without checking for validity.
///
/// # Safety
/// The given ID must be a valid base CAN ID (id <= [`MAX_CAN_ID_BASE`]).
pub const unsafe fn new_unchecked(id: u16) -> Self {
debug_assert!(id <= MAX_CAN_ID_BASE);
Self {
id
}
}
}

impl CanExtendedId {
Expand All @@ -135,6 +255,17 @@ impl CanExtendedId {
pub const fn as_u32(self) -> u32 {
self.id
}

/// Create a new extended CAN ID without checking for validity.
///
/// # Safety
/// The given ID must be a valid extended CAN ID (id <= [`MAX_CAN_ID_EXTENDED`]).
pub const unsafe fn new_unchecked(id: u32) -> Self {
debug_assert!(id <= MAX_CAN_ID_EXTENDED);
Self {
id
}
}
}

impl PartialEq<CanBaseId> for CanId {
Expand Down
Loading

0 comments on commit 1ef288c

Please sign in to comment.