use crate::std;
use std::fmt;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CheckDigit(u8);
impl CheckDigit {
pub const LEN: usize = 1;
pub const fn as_u8(&self) -> u8 {
self.0
}
}
impl From<u8> for CheckDigit {
fn from(b: u8) -> Self {
let digit = std::str::from_utf8(&[b])
.unwrap_or("")
.parse::<u8>()
.unwrap_or(0xff);
Self(digit)
}
}
impl From<CheckDigit> for u8 {
fn from(c: CheckDigit) -> Self {
c.0
}
}
impl From<&CheckDigit> for u8 {
fn from(c: &CheckDigit) -> Self {
(*c).into()
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PartNumberType {
Type1 = 1,
Type2 = 2,
Variant = 3,
Unknown = 0x00,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProjectNumber {
number: u32,
check_digit: CheckDigit,
part_type: PartNumberType,
}
impl ProjectNumber {
pub const LEN: usize = 6;
pub const TYPE_1_LEN: usize = 5;
pub const TYPE_2_LEN: usize = 6;
pub const VARIANT_LEN: usize = 5;
pub const CHECK_DIGIT_IDX: usize = 5;
pub const fn type1(number: u32, check_digit: CheckDigit) -> Self {
Self {
number,
check_digit,
part_type: PartNumberType::Type1,
}
}
pub const fn type2(number: u32) -> Self {
Self {
number,
check_digit: CheckDigit(0xff),
part_type: PartNumberType::Type2,
}
}
pub const fn variant(number: u32, check_digit: CheckDigit) -> Self {
Self {
number,
check_digit,
part_type: PartNumberType::Variant,
}
}
pub const fn zero() -> Self {
Self {
number: 0,
check_digit: CheckDigit(0x00),
part_type: PartNumberType::Unknown,
}
}
pub const fn number(&self) -> u32 {
self.number
}
pub const fn check_digit(&self) -> CheckDigit {
self.check_digit
}
pub const fn part_type(&self) -> PartNumberType {
self.part_type
}
}
impl From<&[u8]> for ProjectNumber {
fn from(b: &[u8]) -> Self {
if b.len() < Self::LEN {
return Self::zero();
}
let type1: u32 = std::str::from_utf8(b[..Self::TYPE_1_LEN].as_ref())
.unwrap_or("")
.parse::<u32>()
.unwrap_or(0);
let type2: u32 = std::str::from_utf8(b[..Self::TYPE_2_LEN].as_ref())
.unwrap_or("")
.parse::<u32>()
.unwrap_or(0);
if (28_000..=28_599).contains(&type1) {
Self::type1(type1, CheckDigit::from(b[Self::CHECK_DIGIT_IDX]))
} else if (49000..=49999).contains(&type1) || (51_000..=52_999).contains(&type1) {
Self::variant(type1, CheckDigit::from(b[Self::CHECK_DIGIT_IDX]))
} else if (286_000..=289_999).contains(&type2) {
Self::type2(type2)
} else {
Self::zero()
}
}
}
impl fmt::Display for ProjectNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.part_type() {
PartNumberType::Type1 => write!(
f,
"{} check digit: {}",
self.number,
self.check_digit.as_u8()
),
PartNumberType::Type2 => write!(f, "{}", self.number),
PartNumberType::Variant => write!(
f,
"{} check digit: {}",
self.number,
self.check_digit.as_u8()
),
PartNumberType::Unknown => write!(f, "Unknown"),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PartVersion(u16);
impl PartVersion {
pub const LEN: usize = 3;
pub fn as_string(&self) -> String {
format!("{self}")
}
}
impl From<&[u8]> for PartVersion {
fn from(b: &[u8]) -> Self {
let version = std::str::from_utf8(b)
.unwrap_or("")
.parse::<u16>()
.unwrap_or(0);
if version > 999 {
Self(0)
} else {
Self(version)
}
}
}
impl<const N: usize> From<[u8; N]> for PartVersion {
fn from(b: [u8; N]) -> Self {
b.as_ref().into()
}
}
impl<const N: usize> From<&[u8; N]> for PartVersion {
fn from(b: &[u8; N]) -> Self {
b.as_ref().into()
}
}
impl fmt::Display for PartVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "V{:.2}", (self.0 as f32) / 100f32)
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BootPartNumber {
project_number: ProjectNumber,
version: PartVersion,
}
impl BootPartNumber {
pub const LEN: usize = 9;
pub const fn new(project_number: ProjectNumber, version: PartVersion) -> Self {
Self {
project_number,
version,
}
}
pub const fn default() -> Self {
Self {
project_number: ProjectNumber::zero(),
version: PartVersion(0),
}
}
}
impl From<&[u8]> for BootPartNumber {
fn from(b: &[u8]) -> Self {
if b.len() < Self::LEN {
Self::default()
} else {
let len = std::cmp::min(b.len(), Self::LEN);
let project_number: ProjectNumber = b[..ProjectNumber::LEN].as_ref().into();
let version: PartVersion = b[ProjectNumber::LEN..len].as_ref().into();
Self {
project_number,
version,
}
}
}
}
impl fmt::Display for BootPartNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Project number: {}, Version: {}",
self.project_number, self.version
)
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ApplicationPartNumber {
project_number: ProjectNumber,
version: PartVersion,
}
impl ApplicationPartNumber {
pub const LEN: usize = 9;
pub const fn new(project_number: ProjectNumber, version: PartVersion) -> Self {
Self {
project_number,
version,
}
}
pub const fn default() -> Self {
Self {
project_number: ProjectNumber::zero(),
version: PartVersion(0),
}
}
}
impl From<&[u8]> for ApplicationPartNumber {
fn from(b: &[u8]) -> Self {
if b.len() < Self::LEN {
Self::default()
} else {
let len = std::cmp::min(b.len(), Self::LEN);
let project_number: ProjectNumber = b[..ProjectNumber::LEN].as_ref().into();
let version: PartVersion = b[ProjectNumber::LEN..len].as_ref().into();
Self {
project_number,
version,
}
}
}
}
impl fmt::Display for ApplicationPartNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Project number: {}, Version: {}",
self.project_number, self.version
)
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct VariantPartNumber {
project_number: ProjectNumber,
version: PartVersion,
}
impl VariantPartNumber {
pub const LEN: usize = 9;
pub const fn new(project_number: ProjectNumber, version: PartVersion) -> Self {
Self {
project_number,
version,
}
}
pub const fn default() -> Self {
Self {
project_number: ProjectNumber::zero(),
version: PartVersion(0),
}
}
}
impl From<&[u8]> for VariantPartNumber {
fn from(b: &[u8]) -> Self {
if b.len() < Self::LEN {
Self::default()
} else {
let len = std::cmp::min(b.len(), Self::LEN);
let project_number: ProjectNumber = b[..ProjectNumber::LEN].as_ref().into();
let version: PartVersion = b[ProjectNumber::LEN..len].as_ref().into();
Self {
project_number,
version,
}
}
}
}
impl fmt::Display for VariantPartNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Project number: {}, Version: {}",
self.project_number, self.version
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn boot_version_parsing() {
let version = PartVersion::from(b"123");
let formatted_version = version.as_string();
assert_eq!(formatted_version, "V1.23");
let version = PartVersion::from(b"23");
let formatted_version = version.as_string();
assert_eq!(formatted_version, "V0.23");
let version = PartVersion::from(b"3");
let formatted_version = version.as_string();
assert_eq!(formatted_version, "V0.03");
let version = PartVersion::from(b"");
let formatted_version = version.as_string();
assert_eq!(formatted_version, "V0.00");
let version = PartVersion::from(b"999888777");
let formatted_version = version.as_string();
assert_eq!(formatted_version, "V0.00");
}
}