From a509436b4ac048a66445a058955693ecede39ba3 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 11 Mar 2026 14:56:38 -0400 Subject: [PATCH 1/6] Updates for first crates.io release --- Cargo.lock | 4 ++-- Cargo.toml | 9 +++++++-- uds_protocol_derive/Cargo.toml | 7 ++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d67101..79366ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,7 +325,7 @@ dependencies = [ [[package]] name = "uds_protocol" -version = "0.0.2" +version = "0.1.0" dependencies = [ "bitmask-enum", "byteorder", @@ -340,7 +340,7 @@ dependencies = [ [[package]] name = "uds_protocol_derive" -version = "0.0.2" +version = "0.1.0" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7a23920..74b07b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,15 @@ [package] name = "uds_protocol" description = "A library for encoding and decoding UDS (ISO 14229) messages" -version = "0.0.2" +version = "0.1.0" edition = "2024" +rust-version = "1.85.0" license = "MIT OR Apache-2.0" repository = "https://github.com/luminartech/uds_protocol" +homepage = "https://github.com/luminartech/uds_protocol" +documentation = "https://docs.rs/uds_protocol" +keywords = ["uds", "iso-14229", "automotive", "diagnostics", "obd"] +categories = ["encoding", "embedded", "hardware-support"] authors = [ "Justin Kovacich", "Kimberly Kryger ", @@ -19,7 +24,7 @@ utoipa = ["dep:utoipa"] clap = ["dep:clap"] [dependencies] -uds_protocol_derive = { path = "./uds_protocol_derive", version = "0.0.2" } +uds_protocol_derive = { path = "./uds_protocol_derive", version = "0.1.0" } bitmask-enum = "2" byteorder = "1" thiserror = "2" diff --git a/uds_protocol_derive/Cargo.toml b/uds_protocol_derive/Cargo.toml index cf79e1a..795f233 100644 --- a/uds_protocol_derive/Cargo.toml +++ b/uds_protocol_derive/Cargo.toml @@ -1,10 +1,15 @@ [package] name = "uds_protocol_derive" description = "Procedural macros for easier implementation of UDS protocol traits" -version = "0.0.2" +version = "0.1.0" edition = "2024" +rust-version = "1.85.0" license = "MIT OR Apache-2.0" repository = "https://github.com/luminartech/uds_protocol" +homepage = "https://github.com/luminartech/uds_protocol" +documentation = "https://docs.rs/uds_protocol_derive" +keywords = ["uds", "iso-14229", "automotive", "diagnostics", "derive"] +categories = ["development-tools::procedural-macro-helpers"] authors = [ "Kimberly Kryger ", "Zachary Heylmun ", ] From 29d6c7f52fdd02e874aa56a6158c8af5dfe3a485 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 11 Mar 2026 14:58:16 -0400 Subject: [PATCH 2/6] check msrv on CI --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd10e1b..d47c930 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,19 @@ jobs: - run: rustup component add rustfmt - run: cargo fmt -- --check + msrv: + name: Check MSRV + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@1.85.0 + - uses: Swatinem/rust-cache@v2 + - name: Build with MSRV + run: cargo build --all-features + - name: Run tests with MSRV + run: cargo test --all-features + semver-checks: runs-on: ubuntu-latest timeout-minutes: 10 From b9fa3da21f6403829b1afed6d12363ed559223c8 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 11 Mar 2026 15:55:37 -0400 Subject: [PATCH 3/6] make enums non_exhaustive to prevent breaking api changes --- src/common/communication_control_type.rs | 1 + src/common/communication_type.rs | 1 + src/common/diagnostic_identifier.rs | 2 ++ src/common/diagnostic_session_type.rs | 1 + src/common/negative_response_code.rs | 1 + src/common/reset_type.rs | 1 + src/common/security_access_type.rs | 1 + src/error.rs | 1 + src/lib.rs | 2 ++ src/request.rs | 1 + src/response.rs | 2 ++ src/services/clear_dtc_information.rs | 1 + src/services/communication_control.rs | 1 + src/services/read_data_by_identifier.rs | 1 + src/services/security_access.rs | 1 + src/services/tester_present.rs | 2 ++ 16 files changed, 20 insertions(+) diff --git a/src/common/communication_control_type.rs b/src/common/communication_control_type.rs index 083d8e8..b7e60b5 100644 --- a/src/common/communication_control_type.rs +++ b/src/common/communication_control_type.rs @@ -10,6 +10,7 @@ use crate::Error; #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum CommunicationControlType { /// This value indicates that the reception and transmission of messages /// shall be enabled for the specified [`CommunicationType`](crate::CommunicationType) diff --git a/src/common/communication_type.rs b/src/common/communication_type.rs index d4b6231..91c0058 100644 --- a/src/common/communication_type.rs +++ b/src/common/communication_type.rs @@ -12,6 +12,7 @@ use crate::Error; #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum CommunicationType { /// This value is reserved by the ISO 14229-1 Specification ISOSAEReserved, diff --git a/src/common/diagnostic_identifier.rs b/src/common/diagnostic_identifier.rs index 6ea6c7d..59d9c36 100644 --- a/src/common/diagnostic_identifier.rs +++ b/src/common/diagnostic_identifier.rs @@ -8,6 +8,7 @@ use crate::{Error, Identifier, traits::RoutineIdentifier}; #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[cfg_attr(feature = "clap", derive(clap::ValueEnum, clap::Parser))] #[derive(Clone, Copy, Eq, Identifier, PartialEq)] +#[non_exhaustive] #[repr(u16)] pub enum UDSIdentifier { /// DID reserved by ISO/SAE (ranges `0x0000–0x00FF`, `0xFF02–0xFFFF`). @@ -209,6 +210,7 @@ impl std::fmt::Debug for UDSIdentifier { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, Identifier, PartialEq)] +#[non_exhaustive] #[repr(u16)] pub enum UDSRoutineIdentifier { /// ISO/SAE reserved routine identifier (`0x0000–0x00FF`, `0xE300–0xEFFF`, `0xFF02–0xFFFF`). diff --git a/src/common/diagnostic_session_type.rs b/src/common/diagnostic_session_type.rs index 70bdb65..0fcad45 100644 --- a/src/common/diagnostic_session_type.rs +++ b/src/common/diagnostic_session_type.rs @@ -10,6 +10,7 @@ use crate::Error; #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum DiagnosticSessionType { /// This value is reserved by the ISO 14229-1 Specification #[cfg_attr(feature = "clap", clap(skip))] diff --git a/src/common/negative_response_code.rs b/src/common/negative_response_code.rs index b7978a0..8b3e32a 100644 --- a/src/common/negative_response_code.rs +++ b/src/common/negative_response_code.rs @@ -3,6 +3,7 @@ #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum NegativeResponseCode { /// This response code shall not be used in a negative response message. /// This positiveResponse parameter value is reserved for server internal implementation diff --git a/src/common/reset_type.rs b/src/common/reset_type.rs index 534c634..26b9185 100644 --- a/src/common/reset_type.rs +++ b/src/common/reset_type.rs @@ -11,6 +11,7 @@ use crate::Error; #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum ResetType { /// This value is reserved #[cfg_attr(feature = "clap", clap(skip))] diff --git a/src/common/security_access_type.rs b/src/common/security_access_type.rs index c53bf29..df965ba 100644 --- a/src/common/security_access_type.rs +++ b/src/common/security_access_type.rs @@ -12,6 +12,7 @@ use crate::Error; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum SecurityAccessType { /// This value is reserved for future definition ISOSAEReserved(u8), diff --git a/src/error.rs b/src/error.rs index 69ff029..9e8a1d0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,7 @@ use thiserror::Error; /// Errors that can occur during UDS message encoding, decoding, or validation. #[derive(Debug, Error)] +#[non_exhaustive] pub enum Error { /// An underlying I/O error occurred while reading or writing. #[error(transparent)] diff --git a/src/lib.rs b/src/lib.rs index 41a1755..ee73aaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ pub type ProtocolResponse = Response; #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum RoutineControlSubFunction { /// Routine will be started sometime between completion of the `StartRoutine` request and the completion of the 1st response message /// which indicates that the routine has already been performed, or is in progress @@ -130,6 +131,7 @@ impl IterableWireFormat for Vec { #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] /// Controls whether the server should enable or disable DTC status-bit updates. /// /// Used by [`ControlDTCSettingsRequest`] to instruct the server. diff --git a/src/request.rs b/src/request.rs index 9e72827..3d71b82 100644 --- a/src/request.rs +++ b/src/request.rs @@ -24,6 +24,7 @@ use super::{ #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Debug, PartialEq)] +#[non_exhaustive] pub enum Request { /// Request to clear diagnostic information. See [`ClearDiagnosticInfoRequest`]. ClearDiagnosticInfo(ClearDiagnosticInfoRequest), diff --git a/src/response.rs b/src/response.rs index c9fb985..de78a02 100644 --- a/src/response.rs +++ b/src/response.rs @@ -11,6 +11,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt}; use std::io::{Read, Write}; /// A raw UDS response consisting of the service type and its unparsed payload bytes. +#[non_exhaustive] pub struct UdsResponse { /// The service this response corresponds to. pub service: UdsServiceType, @@ -22,6 +23,7 @@ pub struct UdsResponse { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Debug, PartialEq)] +#[non_exhaustive] pub enum Response { /// Response to a [`ClearDiagnosticInfoRequest`](crate::ClearDiagnosticInfoRequest) ClearDiagnosticInfo, diff --git a/src/services/clear_dtc_information.rs b/src/services/clear_dtc_information.rs index a80d7eb..2100886 100644 --- a/src/services/clear_dtc_information.rs +++ b/src/services/clear_dtc_information.rs @@ -14,6 +14,7 @@ const CLEAR_DIAG_INFO_NEGATIVE_RESPONSE_CODES: [NegativeResponseCode; 4] = [ #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] pub struct ClearDiagnosticInfoRequest { /// Can be either a DTC group (such as chassis/powertrain) or a single DTC pub group_of_dtc: DTCRecord, diff --git a/src/services/communication_control.rs b/src/services/communication_control.rs index da75b2a..f8713c1 100644 --- a/src/services/communication_control.rs +++ b/src/services/communication_control.rs @@ -21,6 +21,7 @@ const COMMUNICATION_CONTROL_NEGATIVE_RESPONSE_CODES: [NegativeResponseCode; 4] = #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub struct CommunicationControlRequest { control_type: SuppressablePositiveResponse, /// The communication type to apply the control to. diff --git a/src/services/read_data_by_identifier.rs b/src/services/read_data_by_identifier.rs index 356b154..bd3fc99 100644 --- a/src/services/read_data_by_identifier.rs +++ b/src/services/read_data_by_identifier.rs @@ -70,6 +70,7 @@ impl SingleValueWireFormat #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Eq, PartialEq)] +#[non_exhaustive] pub struct ReadDataByIdentifierResponse { /// The decoded payload entries returned by the server. pub data: Vec, diff --git a/src/services/security_access.rs b/src/services/security_access.rs index 78629b3..bae59ee 100644 --- a/src/services/security_access.rs +++ b/src/services/security_access.rs @@ -40,6 +40,7 @@ const SECURITY_ACCESS_NEGATIVE_RESPONSE_CODES: [NegativeResponseCode; 8] = [ #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] pub struct SecurityAccessRequest { access_type: SuppressablePositiveResponse, request_data: Vec, diff --git a/src/services/tester_present.rs b/src/services/tester_present.rs index 32c9d07..eaeccce 100644 --- a/src/services/tester_present.rs +++ b/src/services/tester_present.rs @@ -64,6 +64,7 @@ impl TryFrom for ZeroSubFunction { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub struct TesterPresentRequest { zero_sub_function: SuppressablePositiveResponse, } @@ -125,6 +126,7 @@ impl SingleValueWireFormat for TesterPresentRequest { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub struct TesterPresentResponse { zero_sub_function: ZeroSubFunction, } From d41299c02ee64fff0f69787c72971c2069e2f4eb Mon Sep 17 00:00:00 2001 From: Zach Heylmun Date: Tue, 31 Mar 2026 17:10:20 -0400 Subject: [PATCH 4/6] replace uds_protocol_derive proc-macro crate with macro_rules The derive macro only generated `impl Identifier for T {}`, which is trivially accomplished by a macro_rules macro. This eliminates a separate crate to publish and maintain. --- Cargo.lock | 10 ---------- Cargo.toml | 1 - src/common/diagnostic_identifier.rs | 8 +++++--- src/lib.rs | 19 ++++++++----------- src/protocol_definitions.rs | 5 +++-- src/services/routine_control.rs | 5 +++-- src/services/write_data_by_identifier.rs | 4 +++- src/traits.rs | 20 ++++++++++++++++++-- 8 files changed, 40 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79366ea..546a136 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -334,19 +334,9 @@ dependencies = [ "serde_bytes", "thiserror", "tracing", - "uds_protocol_derive", "utoipa", ] -[[package]] -name = "uds_protocol_derive" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/Cargo.toml b/Cargo.toml index 74b07b7..9628e1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ utoipa = ["dep:utoipa"] clap = ["dep:clap"] [dependencies] -uds_protocol_derive = { path = "./uds_protocol_derive", version = "0.1.0" } bitmask-enum = "2" byteorder = "1" thiserror = "2" diff --git a/src/common/diagnostic_identifier.rs b/src/common/diagnostic_identifier.rs index 59d9c36..06f136c 100644 --- a/src/common/diagnostic_identifier.rs +++ b/src/common/diagnostic_identifier.rs @@ -1,5 +1,5 @@ //! DIDs are used to identify the data that is requested or sent in a diagnostic service. -use crate::{Error, Identifier, traits::RoutineIdentifier}; +use crate::{Error, impl_identifier, traits::RoutineIdentifier}; /// C.1 DID - Diagnostic Data Identifier specified in ISO 14229-1 /// @@ -7,7 +7,7 @@ use crate::{Error, Identifier, traits::RoutineIdentifier}; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[cfg_attr(feature = "clap", derive(clap::ValueEnum, clap::Parser))] -#[derive(Clone, Copy, Eq, Identifier, PartialEq)] +#[derive(Clone, Copy, Eq, PartialEq)] #[non_exhaustive] #[repr(u16)] pub enum UDSIdentifier { @@ -94,6 +94,7 @@ pub enum UDSIdentifier { /// Reserved for ISO 15765-5 (`0xFF01`). ReservedForISO15765_5 = 0xFF01, } +impl_identifier!(UDSIdentifier); impl TryFrom for UDSIdentifier { type Error = Error; @@ -209,7 +210,7 @@ impl std::fmt::Debug for UDSIdentifier { /// and they must be implemented by the tester system. #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] -#[derive(Clone, Copy, Debug, Eq, Identifier, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[non_exhaustive] #[repr(u16)] pub enum UDSRoutineIdentifier { @@ -256,6 +257,7 @@ pub enum UDSRoutineIdentifier { /// 0xFF01 CheckProgrammingDependencies = 0xFF01, } +impl_identifier!(UDSRoutineIdentifier); /// We know all values for the Routine Identifier, so we can implement `From` for `UDSRoutineIdentifier` impl From for UDSRoutineIdentifier { diff --git a/src/lib.rs b/src/lib.rs index ee73aaf..2589d00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,16 @@ #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] #![warn(clippy::pedantic, missing_docs)] -mod common; -pub use common::*; - mod error; pub use error::Error; -// Export the Identifier derive macro -pub use uds_protocol_derive::Identifier; +mod traits; +pub use traits::{ + DiagnosticDefinition, Identifier, IterableWireFormat, RoutineIdentifier, SingleValueWireFormat, + WireFormat, +}; + +mod common; +pub use common::*; mod protocol_definitions; pub use protocol_definitions::{ProtocolIdentifier, ProtocolPayload, ProtocolRoutinePayload}; @@ -24,12 +27,6 @@ pub use service::UdsServiceType; mod services; pub use services::*; -mod traits; -pub use traits::{ - DiagnosticDefinition, Identifier, IterableWireFormat, RoutineIdentifier, SingleValueWireFormat, - WireFormat, -}; - /// UDS positive-response service-ID offset. Added to the request SID to form the response SID. pub const SUCCESS: u8 = 0x80; /// UDS `requestCorrectlyReceivedResponsePending` negative response code (`0x78`). diff --git a/src/protocol_definitions.rs b/src/protocol_definitions.rs index 358cd55..ddad686 100644 --- a/src/protocol_definitions.rs +++ b/src/protocol_definitions.rs @@ -1,5 +1,5 @@ use crate::{ - Error, Identifier, IterableWireFormat, SingleValueWireFormat, UDSIdentifier, + Error, IterableWireFormat, SingleValueWireFormat, UDSIdentifier, impl_identifier, UDSRoutineIdentifier, WireFormat, }; use std::ops::Deref; @@ -8,10 +8,11 @@ use tracing::error; /// Protocol Identifier provides an implementation of Diagnostics Identifiers that only supports Diagnostic Identifiers defined by UDS #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] -#[derive(Clone, Copy, Debug, Eq, Identifier, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct ProtocolIdentifier { identifier: UDSIdentifier, } +impl_identifier!(ProtocolIdentifier); impl ProtocolIdentifier { /// Wrap a [`UDSIdentifier`] in a `ProtocolIdentifier`. diff --git a/src/services/routine_control.rs b/src/services/routine_control.rs index c4a8b26..87004d6 100644 --- a/src/services/routine_control.rs +++ b/src/services/routine_control.rs @@ -146,11 +146,12 @@ impl SingleValueWireFormat #[cfg(test)] mod request { use super::*; - use crate::Identifier; + use crate::impl_identifier; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Clone, Copy, Debug, Eq, Identifier, PartialEq)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] struct TestIdentifier(pub u16); + impl_identifier!(TestIdentifier); impl From for TestIdentifier { fn from(value: u16) -> Self { diff --git a/src/services/write_data_by_identifier.rs b/src/services/write_data_by_identifier.rs index 42ab7fc..91003da 100644 --- a/src/services/write_data_by_identifier.rs +++ b/src/services/write_data_by_identifier.rs @@ -95,13 +95,15 @@ impl SingleValueWireFormat #[cfg(test)] mod test { use super::*; + use crate::impl_identifier; use byteorder::WriteBytesExt; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Clone, Copy, Debug, Identifier, PartialEq)] + #[derive(Clone, Copy, Debug, PartialEq)] pub enum TestIdentifier { Abracadabra = 0xBEEF, } + impl_identifier!(TestIdentifier); impl From for TestIdentifier { fn from(value: u16) -> Self { match value { diff --git a/src/traits.rs b/src/traits.rs index 0688ba7..adc380d 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -109,7 +109,7 @@ mod maybe_utoipa { /// Trait for types that can be used as identifiers (ie Data Identifiers and Routine Identifiers) /// -/// Prefer using the [`#[derive(Identifier)]`](uds_protocol_derive::Identifier) derive macro to implement this trait +/// Use the [`impl_identifier!`] macro to implement this trait for your types. pub trait Identifier: TryFrom + Into + Clone + Copy + maybe_serde::Bound { /// Returns a `Vec` from a reader that contains a list of Identifier values /// # Errors @@ -152,6 +152,21 @@ pub trait Identifier: TryFrom + Into + Clone + Copy + maybe_serde::Bou } } +/// Implement the [`Identifier`] trait for a type. +/// +/// The type must already implement `TryFrom`, `Into`, `Clone`, and `Copy`. +/// +/// # Example +/// ```rust,ignore +/// impl_identifier!(MyIdentifierEnum); +/// ``` +#[macro_export] +macro_rules! impl_identifier { + ($($t:ty),+ $(,)?) => { + $(impl $crate::Identifier for $t {})+ + }; +} + /// Marker subtrait of [`Identifier`] to distinguish routine identifiers from data identifiers. pub trait RoutineIdentifier: Identifier {} @@ -279,7 +294,7 @@ mod tests { use std::io::Cursor; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[derive(Clone, Copy, Debug, Eq, Identifier, PartialEq)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u16)] pub enum MyIdentifier { Identifier1 = 0x0101, @@ -287,6 +302,7 @@ mod tests { Identifier3 = 0x0303, UDSIdentifier(UDSIdentifier), } + impl_identifier!(MyIdentifier); impl From for MyIdentifier { fn from(value: u16) -> Self { From a7c650cee10b33d5046322d910fbf48b4be7d90b Mon Sep 17 00:00:00 2001 From: Zach Heylmun Date: Tue, 31 Mar 2026 17:11:05 -0400 Subject: [PATCH 5/6] remove uds_protocol_derive crate directory No longer needed after replacing the proc-macro with macro_rules. --- uds_protocol_derive/Cargo.lock | 47 --------------- uds_protocol_derive/Cargo.toml | 24 -------- uds_protocol_derive/README.md | 9 --- uds_protocol_derive/src/lib.rs | 102 --------------------------------- 4 files changed, 182 deletions(-) delete mode 100644 uds_protocol_derive/Cargo.lock delete mode 100644 uds_protocol_derive/Cargo.toml delete mode 100644 uds_protocol_derive/README.md delete mode 100644 uds_protocol_derive/src/lib.rs diff --git a/uds_protocol_derive/Cargo.lock b/uds_protocol_derive/Cargo.lock deleted file mode 100644 index fd4f318..0000000 --- a/uds_protocol_derive/Cargo.lock +++ /dev/null @@ -1,47 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "proc-macro2" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "uds_protocol_derive" -version = "0.0.1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/uds_protocol_derive/Cargo.toml b/uds_protocol_derive/Cargo.toml deleted file mode 100644 index 795f233..0000000 --- a/uds_protocol_derive/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "uds_protocol_derive" -description = "Procedural macros for easier implementation of UDS protocol traits" -version = "0.1.0" -edition = "2024" -rust-version = "1.85.0" -license = "MIT OR Apache-2.0" -repository = "https://github.com/luminartech/uds_protocol" -homepage = "https://github.com/luminartech/uds_protocol" -documentation = "https://docs.rs/uds_protocol_derive" -keywords = ["uds", "iso-14229", "automotive", "diagnostics", "derive"] -categories = ["development-tools::procedural-macro-helpers"] -authors = [ -"Kimberly Kryger ", -"Zachary Heylmun ", ] - -[lib] -name = "uds_protocol_derive" -proc-macro = true - -[dependencies] -proc-macro2 = { version="1", features = ["proc-macro"] } -quote = "1" -syn = "2" diff --git a/uds_protocol_derive/README.md b/uds_protocol_derive/README.md deleted file mode 100644 index f8def6b..0000000 --- a/uds_protocol_derive/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Unified Diagnostics Services (UDS) Protocol - -Macro support crate for uds_protocol. -Allows library users to derive the `Identifier` trait for their own diagnostics data types. - -[![Crates.io](https://img.shields.io/crates/v/uds_protocol.svg)](https://crates.io/crates/uds_protocol_derive) -[![Docs.rs](https://docs.rs/uds_protocol/badge.svg)](https://docs.rs/uds_protocol) -[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE-MIT) -[![APACHE License](https://img.shields.io/badge/license-APACHE-blue.svg)](./LICENSE-APACHE) diff --git a/uds_protocol_derive/src/lib.rs b/uds_protocol_derive/src/lib.rs deleted file mode 100644 index 64d4a94..0000000 --- a/uds_protocol_derive/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -#![warn(clippy::pedantic)] -//! Blanket/Common types and traits for identifiers (Data Identifiers and Routine Identifiers) -use proc_macro::TokenStream; -use quote::quote; -use syn::{DeriveInput, parse_macro_input}; - -/// Derive Identifier and implement `TryFrom`, `Into` traits -/// -/// ## Enum Example -/// ```rust -/// use uds_protocol::{UDSRoutineIdentifier, Identifier, Error}; -/// -/// #[derive(Clone, Copy, Identifier, Serialize)] -/// pub enum MyRoutineIdentifier { -/// /// 0x0101 (example) -/// VerifySignature, -/// -/// // Standard ISO UDS routine fallthrough -/// UDSRoutineIdentifier(UDSRoutineIdentifier), -/// } -/// -/// impl TryFrom for MyRoutineIdentifier { -/// type Error = uds_protocol::Error; -/// fn try_from(value: u16) -> Result { -/// match value { -/// 0x0101 => Ok(MyRoutineIdentifier::VerifySignature), -/// _ => Ok(MyRoutineIdentifier::UDSRoutineIdentifier(UDSRoutineIdentifier::try_from(value)?)), -/// } -/// } -/// } -/// -/// impl From for u16 { -/// fn from(value: MyRoutineIdentifier) -> Self { -/// match value { -/// MyRoutineIdentifier::VerifySignature => 0x0101, -/// MyRoutineIdentifier::UDSRoutineIdentifier(identifier) => u16::from(identifier), -/// } -/// } -/// } -/// ``` -/// -/// ## Struct definition Example -/// Structs can only contain a single value to be used as an identifier to constrain the type -/// ```rust -/// -/// use uds_protocol::{UDSIdentifier, Identifier}; -/// -/// #[derive(Clone, Copy, Identifier, Serialize)] -/// pub struct ProtocolIdentifier { -/// identifier: UDSIdentifier, -/// } -/// ``` -/// -/// # Panics -/// -/// This will panic if `syn::Data::Union()` type is passed as input -/// -#[proc_macro_derive(Identifier)] -pub fn uds_identifier_derive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - // Validate shape; on failure, return compile_error! tokens. - if let Err(e) = validate_identifier_shape(&input) { - return e.to_compile_error().into(); - } - - let name = &input.ident; - let expanded = quote! { - impl Identifier for #name {} - }; - - TokenStream::from(expanded) -} - -fn validate_identifier_shape(input: &DeriveInput) -> Result<(), syn::Error> { - match &input.data { - // Accept any enum - syn::Data::Enum(_) => Ok(()), - - // Sometimes we use a struct to simply pass through the identifier, accept those as well - syn::Data::Struct(s) => { - if let syn::Fields::Named(fields) = &s.fields { - if fields.named.len() == 1 { - Ok(()) - } else { - Err(syn::Error::new_spanned( - &s.fields, - "Identifier can only be derived for structs with a single member", - )) - } - } else { - Ok(()) - } - } - - // Reject unions with a nice error (don’t panic) - syn::Data::Union(u) => Err(syn::Error::new_spanned( - u.union_token, - "Identifier can only be derived for enums and structs", - )), - } -} From 393760a4e9cdf23c316361424ff03ef7d751facd Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 1 Apr 2026 09:18:56 -0400 Subject: [PATCH 6/6] cargo fmt --- src/protocol_definitions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protocol_definitions.rs b/src/protocol_definitions.rs index ddad686..8c75718 100644 --- a/src/protocol_definitions.rs +++ b/src/protocol_definitions.rs @@ -1,6 +1,6 @@ use crate::{ - Error, IterableWireFormat, SingleValueWireFormat, UDSIdentifier, impl_identifier, - UDSRoutineIdentifier, WireFormat, + Error, IterableWireFormat, SingleValueWireFormat, UDSIdentifier, UDSRoutineIdentifier, + WireFormat, impl_identifier, }; use std::ops::Deref; use tracing::error;