/// A datagram could not be turned into a [`crate::Message`]. /// /// Everything here is a *structural* failure: the bytes cannot be parsed at /// all. Values that parse but are nonsensical (a latitude of 300°, a timestamp /// in 1970) are not errors at this layer — see [`ValidationError`]. #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] pub enum DecodeError { #[error("datagram too short: {0} bytes, minimum is {min}", min = crate::MIN_DATAGRAM)] TooShort(usize), #[error("datagram too long: {0} bytes, maximum is {max}", max = crate::MAX_DATAGRAM)] TooLong(usize), #[error("unsupported protocol version {0}, this build speaks {ours}", ours = crate::VERSION)] BadVersion(u8), #[error("unknown message type 0x{0:x}")] BadMsgType(u8), /// The AEAD tag did not verify. Never answer this — see /// [`crate::may_respond`] and the "no oracle" rule. #[error("authentication failed")] AuthFailed, #[error("{what}: expected {expected} payload bytes, got {actual}")] BadPayloadLen { what: &'static str, expected: usize, actual: usize, }, #[error("LOC point count {0} out of range 1..={max}", max = crate::MAX_POINTS)] BadPointCount(usize), #[error("ACK nonce count {0} out of range 1..={max}", max = crate::MAX_POINTS)] BadNonceCount(usize), #[error("unknown {field} discriminant {value}")] BadEnum { field: &'static str, value: u8 }, } /// A message parsed cleanly but carries values the server should not store. /// /// Kept separate from [`DecodeError`] on purpose: the codec stays a total /// function over well-formed byte strings, so the round-trip property holds /// without carve-outs, and policy lives where policy belongs. #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] pub enum ValidationError { #[error("latitude {0} out of range ±90e7")] Latitude(i32), #[error("longitude {0} out of range ±180e7")] Longitude(i32), #[error("bearing {0} centidegrees out of range 0..=35999")] Bearing(u16), #[error("battery {0}% out of range 0..=100")] Battery(u8), #[error("timestamp {ts} is {off_by}s outside the accepted window around {now}")] Timestamp { ts: u32, now: u32, off_by: i64 }, }