diff --git a/Cargo.lock b/Cargo.lock index 403afe8..d39acfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cc" -version = "1.2.45" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "shlex", @@ -88,15 +88,15 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "iana-time-zone" @@ -124,19 +124,25 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", ] [[package]] -name = "js-sys" -version = "0.3.82" +name = "itoa" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -144,15 +150,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -224,6 +230,7 @@ dependencies = [ "log", "num_enum", "serde", + "serde_json", "thiserror", ] @@ -242,6 +249,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "serde" version = "1.0.228" @@ -271,6 +284,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + [[package]] name = "shlex" version = "1.3.0" @@ -279,9 +305,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.109" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -319,9 +345,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ "indexmap", "toml_datetime", @@ -352,9 +378,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -365,9 +391,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -375,9 +401,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -388,9 +414,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -456,9 +482,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 443b042..98e033b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,16 +9,14 @@ readme = "README.md" keywords = ["decode", "encode", "serial"] categories = ["encoding", "decoding"] -# [features] -# json = ["serde_json"] - [features] default = ["std"] std = [] +json = ["serde_json"] [dependencies] serde = { version = "1.0"} -# serde_json = { version = "1.0", optional = true } +serde_json = { version = "1.0", optional = true } thiserror = "2.0.17" chrono = { version = "0.4", features = ["serde"] } enum_stringify = "0.6.4" @@ -32,4 +30,3 @@ suspicious = { level = "deny", priority = -1 } perf = { level = "deny", priority = -1 } cargo = "warn" nursery = "warn" -restrictions = "warn" diff --git a/src/lib.rs b/src/lib.rs index a162d32..e8d4d92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,10 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![expect(clippy::implicit_return, reason="Implicit return is fine in this crate")] +#![expect(clippy::exhaustive_structs, reason="All Structs are based on a fixed protocol")] +#![expect(clippy::arbitrary_source_item_ordering, reason="Order is the same as in the protocl")] +#![allow(clippy::question_mark_used, reason="I like ?")] + mod message; mod enums; mod utils; diff --git a/src/message.rs b/src/message.rs index ef15ceb..9ef6a4b 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,16 +1,8 @@ use log::{warn, error}; -// Use std::vec when std exists, otherwise alloc::vec -#[cfg(feature = "std")] -use std::vec; - -#[cfg(not(feature = "std"))] extern crate alloc; -#[cfg(not(feature = "std"))] use alloc::vec; - -#[cfg(not(feature = "std"))] use alloc::vec::Vec; use thiserror::Error; @@ -38,6 +30,7 @@ pub struct Protocol300Message { } impl Protocol300Message { + #[inline] /// Creates a new `Protocol300message` used to request data from the device /// /// # Arguments @@ -60,6 +53,7 @@ impl Protocol300Message { } } + #[inline] /// Creates a new `Protocol300message` used to write data to the device /// /// # Arguments @@ -86,8 +80,25 @@ impl Protocol300Message { }) } + #[inline] /// Decode a byte string into a `Protocol300Message` - pub fn try_from_bytes(bytes: &[u8]) -> Result { + /// + /// Tries to decode a vec of bytes into an `Protocol300Message`. Includes + /// checking for valid start bytes and checking the checksum to detect + /// transmission errors + /// + /// # Errors + /// Returns a `MessageDecodingError` on failure. Can be caused by multiple + /// things (for details look at `MessageDecodingError`): + /// - Invalid Start byte + /// - Length does not match actual length + /// - Invalid values for Message Identifier or Function code + /// - Checksum mismatch + pub fn try_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() < 8 { + return Err(MessageDecodingError::VecTooShort) + } + if bytes[0] != 0x41 { return Err(MessageDecodingError::InvalidTelegramStart(bytes[0])); } @@ -123,7 +134,13 @@ impl Protocol300Message { }, }; - let checksum = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, &data); + let checksum_calculated = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, &data); + let checksum = bytes[telegram_length as usize + 2]; + + // Fail if the checksums do not match + if checksum != checksum_calculated { + return Err(MessageDecodingError::Checksum) + } Ok(Self { telegram_length, @@ -137,6 +154,9 @@ impl Protocol300Message { } + #[must_use] + #[inline] + /// Converts a `Protocol300Message` to a vec of bytes pub fn to_bytes(&self) -> Vec { let telegram_start_byte = 0x41; let address_as_bytes = self.data_address.to_be_bytes(); @@ -157,13 +177,13 @@ impl Protocol300Message { } - // TODO docs and test + #[inline] /// Returns the expected length of the return telegram, include start byte but excluding ACK byte /// /// Currently only implemented for Read and Write requests, as the rest is not needed and/or /// unknown behaviour /// - /// # Errors: + /// # Errors /// - `MessageReturnLengthError` - Describes the error which occured when converting pub const fn get_expected_return_telegram_length(&self) -> Result { match (self.message_identifier, self.function_code) { @@ -213,6 +233,12 @@ pub enum MessageDecodingError { /// Error getting function code enum variant by number #[error("Function Code not valid")] InvalidFunctionCode(#[from] num_enum::TryFromPrimitiveError), + /// Checksum does not match the calculated checksum + #[error("Checksums do not match")] + Checksum, + /// The message vec is too short + #[error("The message vec is too short")] + VecTooShort } #[cfg(test)] @@ -229,7 +255,7 @@ mod tests { #[test] - fn test_new_request_message() { + fn new_request_message_works() { let expected_message = Protocol300Message{ telegram_length: 5, message_identifier: MessageIdentifier::Request, @@ -244,7 +270,7 @@ mod tests { } #[test] - fn test_new_write_message() { + fn new_write_message() { let expected_message = Protocol300Message{ telegram_length: 9, message_identifier: MessageIdentifier::Request, @@ -259,14 +285,14 @@ mod tests { } #[test] - fn test_new_write_message_data_too_long() { + fn new_write_message_data_too_long() { let generated_message = Protocol300Message::try_new_write_message(0x0101, &vec![0x00; 300]); assert!(generated_message.is_err()); assert!(matches!(generated_message.err().unwrap(), MessageCreationError::DataTooLong(_))); } #[test] - fn test_received_message_read_response_from_bytes() { + fn received_message_read_response_from_bytes() { let expected_msg = Protocol300Message{ data_address: 0x5525, telegram_length: 0x09, @@ -282,7 +308,7 @@ mod tests { } #[test] - fn test_received_message_write_response_from_bytes() { + fn received_message_write_response_from_bytes() { let expected_msg = Protocol300Message{ data_address: 0x2101, telegram_length: 0x05, @@ -298,7 +324,7 @@ mod tests { } #[test] - fn test_received_message_read_request_from_bytes() { + fn received_message_read_request_from_bytes() { let expected_msg = Protocol300Message{ data_address: 0x5525, telegram_length: 0x05, @@ -314,7 +340,7 @@ mod tests { } #[test] - fn test_received_message_write_request_from_bytes() { + fn received_message_write_request_from_bytes() { let expected_msg = Protocol300Message{ data_address: 0x2323, telegram_length: 0x06, @@ -330,7 +356,7 @@ mod tests { } // TO BYTES #[test] - fn test_received_message_write_request_to_bytes() { + fn received_message_write_request_to_bytes() { let test_msg = Protocol300Message{ data_address: 0x2323, telegram_length: 0x06, @@ -346,7 +372,7 @@ mod tests { } #[test] - fn test_received_message_read_request_to_bytes() { + fn received_message_read_request_to_bytes() { let test_msg = Protocol300Message{ data_address: 0x5525, telegram_length: 0x05, @@ -362,7 +388,7 @@ mod tests { } #[test] - fn test_received_message_write_response_to_bytes() { + fn received_message_write_response_to_bytes() { let test_msg = Protocol300Message{ data_address: 0x2101, telegram_length: 0x05, @@ -378,7 +404,7 @@ mod tests { } #[test] - fn test_received_message_read_response_to_bytes() { + fn received_message_read_response_to_bytes() { let test_msg = Protocol300Message{ data_address: 0x5525, telegram_length: 0x09, @@ -394,7 +420,7 @@ mod tests { } #[test] - fn test_expected_return_length_read() { + fn expected_return_length_read() { let expected_message = Protocol300Message{ telegram_length: 5, message_identifier: MessageIdentifier::Request, @@ -408,7 +434,7 @@ mod tests { } #[test] - fn test_expected_return_length_write() { + fn expected_return_length_write() { let expected_message = Protocol300Message{ telegram_length: 9, message_identifier: MessageIdentifier::Request, diff --git a/src/utils.rs b/src/utils.rs index e95fdf5..b4d9c17 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,10 +1,15 @@ use crate::enums::{FunctionCode, MessageIdentifier}; +#[expect(clippy::big_endian_bytes, reason="Function is needed for the Protocol 300")] +/// Calculate the checksum for a Protocol 300 message +/// +/// The inputs are seperate as the function is needed without have a construced +/// `Protocol300Message` sometimes. pub fn calculate_checksum(length: u8, message_identifier: MessageIdentifier, function_code: FunctionCode, address: u16, data_length: u8, data: &[u8]) -> u8 { - let mut checksum = 0u8; + let mut checksum: u8 = 0; let [addr_hi, addr_lo] = address.to_be_bytes(); - for byte in [length, message_identifier as u8, function_code as u8, data_length, addr_hi, addr_lo].iter().chain(data) { + for byte in [length, message_identifier.into(), function_code.into(), data_length, addr_hi, addr_lo].iter().chain(data) { checksum = checksum.wrapping_add(*byte); } @@ -12,9 +17,6 @@ pub fn calculate_checksum(length: u8, message_identifier: MessageIdentifier, fun } - - - #[cfg(test)] mod tests { use crate::{enums::{FunctionCode, MessageIdentifier}, message::Protocol300Message, utils::calculate_checksum}; @@ -28,7 +30,7 @@ mod tests { use alloc::vec::Vec; #[test] - fn test_check_checksum_1() { + fn check_checksum_1() { let test_msg = Protocol300Message { data_address: 0x5525, telegram_length: 0x05, @@ -42,7 +44,7 @@ mod tests { } #[test] - fn test_check_checksum_2() { + fn check_checksum_2() { let test_msg = Protocol300Message { data_address: 0x5525, telegram_length: 0x07, @@ -56,7 +58,7 @@ mod tests { } #[test] - fn test_received_message_wrapping_add_from_bytes() { + fn received_message_wrapping_add_from_bytes() { let checksum = calculate_checksum(0x06, MessageIdentifier::Request, FunctionCode::VirtualWRITE, 0x2323, 0x01, &[0xB6]); assert_eq!(checksum, 0x05); }