pub const RESULT_COUNT_LEN: usize = 4;
pub const RESULT_ENTRY_LEN: usize = 8;
fn read_u32(reply_body: &[u8], offset: usize) -> Option<u32> {
reply_body
.get(offset..offset + 4)
.and_then(|bytes| bytes.try_into().ok())
.map(u32::from_le_bytes)
}
#[must_use]
pub fn result_code(reply_body: &[u8]) -> Option<u32> {
let count = read_u32(reply_body, 0)?;
if count == 0 {
Some(0)
} else {
read_u32(reply_body, RESULT_COUNT_LEN + 4)
}
}
#[must_use]
pub fn result_section_len(reply_body: &[u8]) -> Option<usize> {
let count = read_u32(reply_body, 0)? as usize;
let len = RESULT_COUNT_LEN + count * RESULT_ENTRY_LEN;
(reply_body.len() >= len).then_some(len)
}
#[cfg(test)]
mod tests {
use super::*;
fn success_body(payload: &[u8]) -> Vec<u8> {
let mut body = 0u32.to_le_bytes().to_vec();
body.extend_from_slice(payload);
body
}
fn rejection_body(code: u32) -> Vec<u8> {
let mut body = 1u32.to_le_bytes().to_vec();
body.extend_from_slice(&0u32.to_le_bytes());
body.extend_from_slice(&code.to_le_bytes());
body
}
#[test]
fn success_decodes_to_zero_and_payload_starts_after_count() {
let body = success_body(b"payload");
assert_eq!(result_code(&body), Some(0));
assert_eq!(result_section_len(&body), Some(RESULT_COUNT_LEN));
assert_eq!(&body[RESULT_COUNT_LEN..], b"payload");
}
#[test]
fn rejection_decodes_to_first_entry_result() {
let body = rejection_body(1009);
assert_eq!(result_code(&body), Some(1009));
assert_eq!(
result_section_len(&body),
Some(RESULT_COUNT_LEN + RESULT_ENTRY_LEN),
);
}
#[test]
fn malformed_body_is_none_never_zero() {
assert_eq!(result_code(&[]), None);
assert_eq!(result_section_len(&[]), None);
assert_eq!(result_code(&1u32.to_le_bytes()), None); assert_eq!(result_section_len(&1u32.to_le_bytes()), None);
assert_eq!(result_code(&[1, 0, 0, 0, 0, 0, 0, 0]), None); assert_eq!(result_section_len(&[1, 0, 0, 0, 0, 0, 0, 0]), None);
}
}