Add Svelte GUI, FIT encoder and README
Standalone binary embeds the frontend, avoiding the dev-server dependency that made the window fail to load. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+48
-12
@@ -35,7 +35,8 @@ pub const PROFILE_VERSION: u16 = 21_205;
|
||||
pub const DATA_TYPE: &[u8; 4] = b".FIT";
|
||||
|
||||
/// FIT base type identifiers. The high bit marks an endian-sensitive type; the
|
||||
/// low 5 bits are the type number.
|
||||
/// low 5 bits are the type number. Variant names are the FIT type names.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum BaseType {
|
||||
@@ -54,7 +55,9 @@ pub enum BaseType {
|
||||
Byte = 0x0D,
|
||||
}
|
||||
|
||||
/// One encoded field value, carrying its own base type and width.
|
||||
/// One encoded field value, carrying its own base type and width. Variant
|
||||
/// names mirror the FIT base types they encode to.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
Enum(u8),
|
||||
@@ -176,9 +179,8 @@ impl Message {
|
||||
self.fields.is_empty()
|
||||
}
|
||||
|
||||
/// The definition-message shape of this message: (field number, size, base
|
||||
/// type) per field. Two messages sharing a shape can share a definition.
|
||||
fn shape(&self) -> Vec<(u8, u8, u8)> {
|
||||
/// The definition-message shape of this message.
|
||||
fn shape(&self) -> Shape {
|
||||
self.fields
|
||||
.iter()
|
||||
.map(|(n, v)| (*n, v.size(), v.base_type() as u8))
|
||||
@@ -186,6 +188,10 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
/// A definition-message shape: `(field number, size in bytes, base type)` per
|
||||
/// field. Two messages sharing a shape can share a definition.
|
||||
type Shape = Vec<(u8, u8, u8)>;
|
||||
|
||||
/// Accumulates data records and emits a complete FIT file.
|
||||
///
|
||||
/// Definitions are cached per local message type, so a definition is re-emitted
|
||||
@@ -195,7 +201,7 @@ impl Message {
|
||||
pub struct FitEncoder {
|
||||
data: Vec<u8>,
|
||||
/// Cached definition shape per local message type (0..16).
|
||||
defs: [Option<(u16, Vec<(u8, u8, u8)>)>; 16],
|
||||
defs: [Option<(u16, Shape)>; 16],
|
||||
message_count: usize,
|
||||
}
|
||||
|
||||
@@ -345,18 +351,48 @@ pub fn verify(bytes: &[u8]) -> Result<(), VerifyError> {
|
||||
/// Why [`verify`] rejected a file.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
|
||||
pub enum VerifyError {
|
||||
/// Shorter than the smallest legal file (header plus CRC).
|
||||
#[error("file is {0} bytes, too short to be a FIT file")]
|
||||
TooShort(usize),
|
||||
TooShort(
|
||||
/// Actual file length.
|
||||
usize,
|
||||
),
|
||||
/// Byte 0 is neither 12 nor 14.
|
||||
#[error("header size {0} is neither 12 nor 14")]
|
||||
BadHeaderSize(u8),
|
||||
BadHeaderSize(
|
||||
/// The declared header size.
|
||||
u8,
|
||||
),
|
||||
/// Bytes 8..12 are not `.FIT`.
|
||||
#[error("data type signature is {0:?}, expected \".FIT\"")]
|
||||
BadSignature([u8; 4]),
|
||||
BadSignature(
|
||||
/// The bytes found where the signature should be.
|
||||
[u8; 4],
|
||||
),
|
||||
/// The header's data size does not match the bytes actually present.
|
||||
#[error("header declares {declared} data bytes but the file carries {actual}")]
|
||||
DataSizeMismatch { declared: usize, actual: usize },
|
||||
DataSizeMismatch {
|
||||
/// Data size from the header.
|
||||
declared: usize,
|
||||
/// Data bytes actually present.
|
||||
actual: usize,
|
||||
},
|
||||
/// The header CRC does not check out.
|
||||
#[error("header CRC is {stored:#06x}, computed {computed:#06x}")]
|
||||
HeaderCrc { stored: u16, computed: u16 },
|
||||
HeaderCrc {
|
||||
/// CRC read from the file.
|
||||
stored: u16,
|
||||
/// CRC computed over bytes 0..12.
|
||||
computed: u16,
|
||||
},
|
||||
/// The trailing file CRC does not check out.
|
||||
#[error("file CRC is {stored:#06x}, computed {computed:#06x}")]
|
||||
FileCrc { stored: u16, computed: u16 },
|
||||
FileCrc {
|
||||
/// CRC read from the end of the file.
|
||||
stored: u16,
|
||||
/// CRC computed over header plus data.
|
||||
computed: u16,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user