use thiserror::Error;
#[derive(Debug, Error)]
pub enum AprError {
#[error("Invalid APR file: expected magic 'APNR', got {found:?}")]
InvalidMagic {
found: [u8; 4],
},
#[error("Unsupported APR version: {version} (supported: {min_supported}+)")]
UnsupportedVersion {
version: u16,
min_supported: u16,
},
#[error("APR file corrupted: checksum mismatch (expected {expected:08x}, got {computed:08x})")]
ChecksumMismatch {
expected: u32,
computed: u32,
},
#[error("Model too large: {size} bytes (max: {max} bytes)")]
ModelTooLarge {
size: usize,
max: usize,
},
#[error("APR file too small: {size} bytes (minimum header: 10 bytes)")]
FileTooSmall {
size: usize,
},
#[error("Invalid model name: '{name}' (must be 3-50 alphanumeric/hyphen chars)")]
InvalidName {
name: String,
},
#[error("Invalid version string: {version}")]
InvalidVersion {
version: String,
},
#[error("CBOR encoding error: {0}")]
CborEncode(String),
#[error("CBOR decoding error: {0}")]
CborDecode(String),
#[error("Compression error: {0}")]
Compression(String),
#[error("Decompression error: {0}")]
Decompression(String),
#[error("Unknown builtin model: '{name}' (available: chase, patrol, wander)")]
UnknownBuiltin {
name: String,
},
#[error("Missing required field: {field}")]
MissingField {
field: &'static str,
},
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
}
impl AprError {
#[must_use]
pub fn invalid_magic(bytes: &[u8]) -> Self {
let mut found = [0u8; 4];
let len = bytes.len().min(4);
found[..len].copy_from_slice(&bytes[..len]);
Self::InvalidMagic { found }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = AprError::InvalidMagic { found: *b"BAAD" };
assert!(err.to_string().contains("APNR"));
}
#[test]
fn test_checksum_error_hex_format() {
let err = AprError::ChecksumMismatch {
expected: 0xDEAD_BEEF,
computed: 0xCAFE_BABE,
};
let msg = err.to_string();
assert!(msg.contains("deadbeef"));
assert!(msg.contains("cafebabe"));
}
#[test]
fn test_model_too_large_error() {
let err = AprError::ModelTooLarge {
size: 2_000_000,
max: 1_048_576,
};
assert!(err.to_string().contains("2000000"));
assert!(err.to_string().contains("1048576"));
}
}