Skip to main content

fastboop_core/
devpro.rs

1use alloc::vec::Vec;
2
3use fastboop_schema::bin::{
4    DEV_PROFILE_BIN_FORMAT_V0, DEV_PROFILE_BIN_V0_HEADER_LEN, DEV_PROFILE_BIN_V0_MAGIC,
5    DeviceProfileBin,
6};
7
8pub use fastboop_schema::*;
9
10#[derive(Debug)]
11pub enum DevProfileCodecError {
12    Decode(postcard::Error),
13    InvalidMagic,
14    UnsupportedFormatVersion(u16),
15}
16
17impl core::fmt::Display for DevProfileCodecError {
18    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19        match self {
20            Self::Decode(err) => write!(f, "decode dev profile: {err}"),
21            Self::InvalidMagic => {
22                write!(
23                    f,
24                    "invalid dev profile magic (expected {DEV_PROFILE_BIN_V0_MAGIC:?})"
25                )
26            }
27            Self::UnsupportedFormatVersion(version) => {
28                write!(
29                    f,
30                    "unsupported dev profile format version {version} (expected {DEV_PROFILE_BIN_FORMAT_V0})"
31                )
32            }
33        }
34    }
35}
36
37impl From<postcard::Error> for DevProfileCodecError {
38    fn from(err: postcard::Error) -> Self {
39        Self::Decode(err)
40    }
41}
42
43pub fn decode_dev_profile(bytes: &[u8]) -> Result<DeviceProfile, DevProfileCodecError> {
44    let Some(format_version) = dev_profile_bin_header_version(bytes) else {
45        return Err(DevProfileCodecError::InvalidMagic);
46    };
47    if format_version != DEV_PROFILE_BIN_FORMAT_V0 {
48        return Err(DevProfileCodecError::UnsupportedFormatVersion(
49            format_version,
50        ));
51    }
52
53    let payload = &bytes[DEV_PROFILE_BIN_V0_HEADER_LEN..];
54    let profile: DeviceProfileBin = postcard::from_bytes(payload)?;
55    Ok(DeviceProfile::from(profile))
56}
57
58pub fn decode_dev_profile_prefix(
59    bytes: &[u8],
60) -> Result<(DeviceProfile, usize), DevProfileCodecError> {
61    let Some(format_version) = dev_profile_bin_header_version(bytes) else {
62        return Err(DevProfileCodecError::InvalidMagic);
63    };
64    if format_version != DEV_PROFILE_BIN_FORMAT_V0 {
65        return Err(DevProfileCodecError::UnsupportedFormatVersion(
66            format_version,
67        ));
68    }
69
70    let payload = &bytes[DEV_PROFILE_BIN_V0_HEADER_LEN..];
71    let (profile, remaining): (DeviceProfileBin, &[u8]) = postcard::take_from_bytes(payload)?;
72    let consumed = DEV_PROFILE_BIN_V0_HEADER_LEN
73        .checked_add(payload.len() - remaining.len())
74        .expect("dev profile consumed length overflow");
75    Ok((DeviceProfile::from(profile), consumed))
76}
77
78pub fn encode_dev_profile(profile: &DeviceProfile) -> Result<Vec<u8>, postcard::Error> {
79    let payload = postcard::to_allocvec(&DeviceProfileBin::from(profile.clone()))?;
80    let mut out = Vec::with_capacity(DEV_PROFILE_BIN_V0_HEADER_LEN + payload.len());
81    out.extend_from_slice(&DEV_PROFILE_BIN_V0_MAGIC);
82    out.extend_from_slice(&DEV_PROFILE_BIN_FORMAT_V0.to_le_bytes());
83    out.extend_from_slice(&payload);
84    Ok(out)
85}
86
87pub fn dev_profile_bin_header_version(bytes: &[u8]) -> Option<u16> {
88    if bytes.len() < DEV_PROFILE_BIN_V0_HEADER_LEN {
89        return None;
90    }
91    if bytes[..DEV_PROFILE_BIN_V0_MAGIC.len()] != DEV_PROFILE_BIN_V0_MAGIC {
92        return None;
93    }
94    Some(u16::from_le_bytes([
95        bytes[DEV_PROFILE_BIN_V0_MAGIC.len()],
96        bytes[DEV_PROFILE_BIN_V0_MAGIC.len() + 1],
97    ]))
98}