mod chunk;
mod header;
mod reader;
pub(crate) mod wire;
mod writer;
pub use chunk::{Chunk, kind};
pub use header::{ConfigEcho, Header, SessionMeta, SnapshotPolicy};
pub use reader::{ChunkIter, RecReader};
pub use writer::RecWriter;
pub const MAGIC: [u8; 4] = *b"TKWS";
pub const TRAILER_MAGIC: [u8; 4] = *b"TKWE";
pub const FORMAT_VERSION: u16 = 1;
pub(crate) const TRAILER_LEN: u64 = 8 + 8 + 8 + 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IndexEntry {
pub kind: u16,
pub first_tick: u64,
pub offset: u64,
pub len: u32,
}
#[derive(Debug)]
pub enum FormatError {
Io(std::io::Error),
BadMagic([u8; 4]),
BadTrailerMagic([u8; 4]),
UnsupportedVersion(u16),
Truncated,
TooLarge,
InvalidUtf8,
ChecksumMismatch {
stored: u64,
computed: u64,
},
Corrupt(&'static str),
}
impl std::fmt::Display for FormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(err) => write!(f, "io error: {err}"),
Self::BadMagic(found) => {
write!(
f,
"not a .rec file, expected TKWS magic, found {found:02x?}"
)
}
Self::BadTrailerMagic(found) => {
write!(f, "missing TKWE trailer magic, found {found:02x?}")
}
Self::UnsupportedVersion(version) => write!(
f,
"format version {version} is newer than this build supports, \
upgrade tickwise to read this file"
),
Self::Truncated => write!(f, "data ends unexpectedly, the file is truncated"),
Self::TooLarge => write!(f, "a declared length exceeds the format safety limits"),
Self::InvalidUtf8 => write!(f, "a string field is not valid utf-8"),
Self::ChecksumMismatch { stored, computed } => write!(
f,
"checksum mismatch, stored {stored:016x} but computed {computed:016x}, \
the file is corrupt"
),
Self::Corrupt(reason) => write!(f, "corrupt file: {reason}"),
}
}
}
impl std::error::Error for FormatError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(err) => Some(err),
_ => None,
}
}
}
impl From<std::io::Error> for FormatError {
fn from(err: std::io::Error) -> Self {
if err.kind() == std::io::ErrorKind::UnexpectedEof {
Self::Truncated
} else {
Self::Io(err)
}
}
}