Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove panicking behavior #17

Closed
wants to merge 4 commits into from
Closed
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
32 changes: 19 additions & 13 deletions src/crc8.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
//! Helper functions for CRC8 checksum validation

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Error {
WrongBufferSize,
WrongCrc,
}

/// Calculate the CRC8 checksum.
pub fn calculate(data: &[u8]) -> u8 {
const CRC8_POLYNOMIAL: u8 = 0x31;
Expand All @@ -21,17 +27,17 @@ pub fn calculate(data: &[u8]) -> u8 {
///
/// The buffer must be in the form of `[d0, d1, crc01, d2, d3, crc23, ...]` where every third byte
/// is the checksum byte of the previous two bytes
/// If the checksum is wrong, return `Err`.
///
/// # Panics
/// If any checksum is wrong, return `Error::WrongCrc`.
///
/// This method will consider every third byte a checksum byte. If the buffer size is not a
/// multiple of 3, then it will panic.
pub fn validate(buf: &[u8]) -> Result<(), ()> {
assert!(buf.len() % 3 == 0, "Buffer must be a multiple of 3");
/// multiple of 3, then it will return `Error::WrongBufferSize`
pub fn validate(buf: &[u8]) -> Result<(), Error> {
if buf.len() % 3 != 0 {
return Err(Error::WrongBufferSize);
}
for chunk in buf.chunks(3) {
if calculate(&[chunk[0], chunk[1]]) != chunk[2] {
return Err(());
return Err(Error::WrongCrc);
}
}
Ok(())
Expand All @@ -40,6 +46,7 @@ pub fn validate(buf: &[u8]) -> Result<(), ()> {
#[cfg(test)]
mod tests {
use crate::crc8;
use crate::crc8::Error;

/// Test the crc function against the test value provided in the SHTC3 datasheet (section
/// 5.10).
Expand All @@ -50,22 +57,21 @@ mod tests {
}

#[test]
fn crc8_validate_empty() {
crc8::validate(&[]).unwrap();
fn crc8_validate_empty_ok() {
assert!(crc8::validate(&[]).is_ok());
}

#[test]
#[should_panic]
fn crc8_validate_not_enough_data() {
crc8::validate(&[0xbe]).unwrap();
assert_eq!(Err(Error::WrongBufferSize), crc8::validate(&[0xbe]));
}

#[test]
fn crc8_validate() {
// Valid CRC
crc8::validate(&[0xbe, 0xef, 0x92]).unwrap();
assert!(crc8::validate(&[0xbe, 0xef, 0x92]).is_ok());

// Invalid CRC
assert_eq!(crc8::validate(&[0xbe, 0xef, 0x91]), Err(()));
assert_eq!(crc8::validate(&[0xbe, 0xef, 0x91]), Err(Error::WrongCrc));
}
}
24 changes: 7 additions & 17 deletions src/i2c.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
//! Helper functions for the u16 word based I²C communication.

use crate::crc8;
pub use crate::Error;
use embedded_hal::blocking::i2c;

/// All possible errors in this crate
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Error<I2cWrite: i2c::Write, I2cRead: i2c::Read> {
I2cWrite(I2cWrite::Error),
I2cRead(I2cRead::Error),
Crc,
}

/// Write an u16 command to the I²C bus.
pub fn write_command<I2cWrite: i2c::Write>(
i2c: &mut I2cWrite,
Expand All @@ -24,21 +17,18 @@ pub fn write_command<I2cWrite: i2c::Write>(
///
/// If the checksum is wrong, return `Error::Crc`.
///
/// # Panics
///
/// This method will consider every third byte a checksum byte. If the buffer size is not a
/// multiple of 3, then it will panic.
/// multiple of 3, then it will return `Error::WrongBufferSize`
pub fn read_words_with_crc<I2c: i2c::Read + i2c::Write>(
i2c: &mut I2c,
addr: u8,
data: &mut [u8],
) -> Result<(), Error<I2c, I2c>> {
assert!(
data.len() % 3 == 0,
"Buffer must hold a multiple of 3 bytes"
);
if data.len() % 3 != 0 {
return Err(Error::WrongBufferSize);
}
i2c.read(addr, data).map_err(Error::I2cRead)?;
crc8::validate(data).map_err(|_| Error::Crc)
Ok(crc8::validate(data)?)
}

#[cfg(test)]
Expand All @@ -62,7 +52,7 @@ mod tests {
let expectations = [Transaction::read(0x58, vec![0xBE, 0xEF, 0x00])];
let mut mock = I2cMock::new(&expectations);
match i2c::read_words_with_crc(&mut mock, 0x58, &mut buf) {
Err(i2c::Error::Crc) => {}
Err(i2c::Error::WrongCrc) => {}
Err(_) => panic!("Invalid error: Must be Crc"),
Ok(_) => panic!("CRC check did not fail"),
}
Expand Down
21 changes: 21 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,26 @@
#![deny(unsafe_code)]
#![cfg_attr(not(test), no_std)]

use embedded_hal::blocking::i2c::Read;
use embedded_hal::blocking::i2c::Write;

/// All possible errors in this crate
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Error<I2cWrite: Write, I2cRead: Read> {
I2cWrite(I2cWrite::Error),
I2cRead(I2cRead::Error),
WrongBufferSize,
WrongCrc,
}

impl<I2cWrite: Write, I2cRead: Read> From<crc8::Error> for Error<I2cWrite, I2cRead> {
fn from(err: crc8::Error) -> Self {
match err {
crc8::Error::WrongBufferSize => Error::WrongBufferSize,
crc8::Error::WrongCrc => Error::WrongCrc,
}
}
}

pub mod crc8;
pub mod i2c;