#![no_std]
#![warn(missing_docs)]
pub mod crc;
pub mod frame;
pub(crate) mod sync;
pub use frame::{Data, EraseData, InfoData, MAX_PAYLOAD, VerifyData};
pub const fn pack_version(major: u8, minor: u8, patch: u8) -> u16 {
((major as u16) << 11) | ((minor as u16) << 6) | (patch as u16)
}
pub const fn unpack_version(v: u16) -> (u8, u8, u8) {
let major = (v >> 11) as u8 & 0x1F;
let minor = (v >> 6) as u8 & 0x1F;
let patch = v as u8 & 0x3F;
(major, minor, patch)
}
pub const fn const_parse_u8(s: &str) -> u8 {
let bytes = s.as_bytes();
let mut i = 0;
let mut result: u16 = 0;
while i < bytes.len() {
let d = bytes[i];
assert!(d >= b'0' && d <= b'9', "non-digit in version string");
result = result * 10 + (d - b'0') as u16;
i += 1;
}
assert!(result <= 255, "version component exceeds u8");
result as u8
}
#[macro_export]
macro_rules! pkg_version {
() => {
$crate::pack_version(
$crate::const_parse_u8(env!("CARGO_PKG_VERSION_MAJOR")),
$crate::const_parse_u8(env!("CARGO_PKG_VERSION_MINOR")),
$crate::const_parse_u8(env!("CARGO_PKG_VERSION_PATCH")),
)
};
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Cmd {
Info = 0x00,
Erase = 0x01,
Write = 0x02,
Verify = 0x03,
Reset = 0x04,
}
impl Cmd {
pub fn is_valid(b: u8) -> bool {
b <= 0x04
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Status {
Request = 0x00,
Ok = 0x01,
WriteError = 0x02,
CrcMismatch = 0x03,
AddrOutOfBounds = 0x04,
Unsupported = 0x05,
PayloadOverflow = 0x06,
}
impl Status {
pub fn is_valid(b: u8) -> bool {
b <= 0x06
}
}
#[derive(Debug, PartialEq)]
pub struct ReadError;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cmd_is_valid() {
assert!(Cmd::is_valid(Cmd::Info as u8));
assert!(Cmd::is_valid(Cmd::Reset as u8));
assert!(!Cmd::is_valid(0x05));
assert!(!Cmd::is_valid(0xFF));
}
#[test]
fn status_is_valid() {
assert!(Status::is_valid(Status::Request as u8));
assert!(Status::is_valid(Status::Unsupported as u8));
assert!(Status::is_valid(Status::PayloadOverflow as u8));
assert!(!Status::is_valid(0x07));
assert!(!Status::is_valid(0xFF));
}
#[test]
fn pack_unpack_round_trip() {
assert_eq!(unpack_version(pack_version(0, 0, 1)), (0, 0, 1));
assert_eq!(unpack_version(pack_version(1, 2, 3)), (1, 2, 3));
assert_eq!(unpack_version(pack_version(31, 31, 63)), (31, 31, 63));
assert_eq!(pack_version(0, 0, 0), 0);
}
#[test]
fn erased_flash_sentinel() {
let (m, n, p) = unpack_version(0xFFFF);
assert_eq!((m, n, p), (31, 31, 63));
}
#[test]
fn pkg_version_macro() {
let v = pkg_version!();
let expected = pack_version(
const_parse_u8(env!("CARGO_PKG_VERSION_MAJOR")),
const_parse_u8(env!("CARGO_PKG_VERSION_MINOR")),
const_parse_u8(env!("CARGO_PKG_VERSION_PATCH")),
);
assert_eq!(v, expected);
}
}