Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 1 addition & 11 deletions Cargo.lock

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

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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<justin.kovacich@luminartech.com>",
"Kimberly Kryger <kim.kryger@luminartech.com>",
Expand All @@ -19,7 +24,6 @@ utoipa = ["dep:utoipa"]
clap = ["dep:clap"]

[dependencies]
uds_protocol_derive = { path = "./uds_protocol_derive", version = "0.0.2" }
bitmask-enum = "2"
byteorder = "1"
thiserror = "2"
Expand Down
1 change: 1 addition & 0 deletions src/common/communication_control_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/common/communication_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 7 additions & 3 deletions src/common/diagnostic_identifier.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//! 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
///
/// The identifiers listed here are defined and should be implemented by the vehicle manufacturer/system supplier.
#[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 {
/// DID reserved by ISO/SAE (ranges `0x0000–0x00FF`, `0xFF02–0xFFFF`).
Expand Down Expand Up @@ -93,6 +94,7 @@ pub enum UDSIdentifier {
/// Reserved for ISO 15765-5 (`0xFF01`).
ReservedForISO15765_5 = 0xFF01,
}
impl_identifier!(UDSIdentifier);

impl TryFrom<u16> for UDSIdentifier {
type Error = Error;
Expand Down Expand Up @@ -208,7 +210,8 @@ 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 {
/// ISO/SAE reserved routine identifier (`0x0000–0x00FF`, `0xE300–0xEFFF`, `0xFF02–0xFFFF`).
Expand Down Expand Up @@ -254,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<u16>` for `UDSRoutineIdentifier`
impl From<u16> for UDSRoutineIdentifier {
Expand Down
1 change: 1 addition & 0 deletions src/common/diagnostic_session_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down
1 change: 1 addition & 0 deletions src/common/negative_response_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/common/reset_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down
1 change: 1 addition & 0 deletions src/common/security_access_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
21 changes: 10 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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`).
Expand Down Expand Up @@ -62,6 +59,7 @@ pub type ProtocolResponse = Response<UdsSpec>;
#[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
Expand Down Expand Up @@ -130,6 +128,7 @@ impl IterableWireFormat for Vec<u8> {
#[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.
Expand Down
7 changes: 4 additions & 3 deletions src/protocol_definitions.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use crate::{
Error, Identifier, IterableWireFormat, SingleValueWireFormat, UDSIdentifier,
UDSRoutineIdentifier, WireFormat,
Error, IterableWireFormat, SingleValueWireFormat, UDSIdentifier, UDSRoutineIdentifier,
WireFormat, impl_identifier,
};
use std::ops::Deref;
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`.
Expand Down
1 change: 1 addition & 0 deletions src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<D: DiagnosticDefinition> {
/// Request to clear diagnostic information. See [`ClearDiagnosticInfoRequest`].
ClearDiagnosticInfo(ClearDiagnosticInfoRequest),
Expand Down
2 changes: 2 additions & 0 deletions src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<D: DiagnosticDefinition> {
/// Response to a [`ClearDiagnosticInfoRequest`](crate::ClearDiagnosticInfoRequest)
ClearDiagnosticInfo,
Expand Down
1 change: 1 addition & 0 deletions src/services/clear_dtc_information.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/services/communication_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<CommunicationControlType>,
/// The communication type to apply the control to.
Expand Down
1 change: 1 addition & 0 deletions src/services/read_data_by_identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ impl<DataIdentifier: Identifier> 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<UserPayload> {
/// The decoded payload entries returned by the server.
pub data: Vec<UserPayload>,
Expand Down
5 changes: 3 additions & 2 deletions src/services/routine_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,12 @@ impl<RoutineStatusRecord: SingleValueWireFormat> 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<u16> for TestIdentifier {
fn from(value: u16) -> Self {
Expand Down
1 change: 1 addition & 0 deletions src/services/security_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<SecurityAccessType>,
request_data: Vec<u8>,
Expand Down
2 changes: 2 additions & 0 deletions src/services/tester_present.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ impl TryFrom<u8> 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<ZeroSubFunction>,
}
Expand Down Expand Up @@ -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,
}
Expand Down
4 changes: 3 additions & 1 deletion src/services/write_data_by_identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,15 @@ impl<DataIdentifier: Identifier> 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<u16> for TestIdentifier {
fn from(value: u16) -> Self {
match value {
Expand Down
20 changes: 18 additions & 2 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u16> + Into<u16> + Clone + Copy + maybe_serde::Bound {
/// Returns a `Vec<Self>` from a reader that contains a list of Identifier values
/// # Errors
Expand Down Expand Up @@ -152,6 +152,21 @@ pub trait Identifier: TryFrom<u16> + Into<u16> + Clone + Copy + maybe_serde::Bou
}
}

/// Implement the [`Identifier`] trait for a type.
///
/// The type must already implement `TryFrom<u16>`, `Into<u16>`, `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 {}

Expand Down Expand Up @@ -279,14 +294,15 @@ 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,
Identifier2 = 0x0202,
Identifier3 = 0x0303,
UDSIdentifier(UDSIdentifier),
}
impl_identifier!(MyIdentifier);

impl From<u16> for MyIdentifier {
fn from(value: u16) -> Self {
Expand Down
Loading
Loading