1use std::{io::Read, path::Path};
13
14use flate2::read::ZlibDecoder;
15
16use crate::Error;
17
18pub fn read_rds_file(path: impl AsRef<Path>) -> Result<rd_rds::RObject, Error> {
21 let path = path.as_ref();
22 rd_rds::file::read(path).map_err(|error| match error {
23 rd_rds::file::ReadError::Io { path, source } => Error::io(path, source),
24 rd_rds::file::ReadError::UnknownEnvelope { magic } => Error::UnsupportedCompression {
25 path: path.to_path_buf(),
26 magic,
27 },
28 rd_rds::file::ReadError::Decode(error) => Error::Rds(error),
29 error => Error::RdsFile(error),
30 })
31}
32
33pub fn decode_rdb_record(bytes: &[u8]) -> Result<rd_rds::RObject, Error> {
38 if bytes.len() < 4 {
39 return Err(Error::RecordSizeMismatch {
41 expected: 4,
42 actual: bytes.len(),
43 });
44 }
45 let (prefix, payload) = bytes.split_at(4);
46 let declared_len =
47 u32::from_be_bytes(prefix.try_into().expect("split_at(4) yields 4 bytes")) as usize;
48
49 let mut decoder = ZlibDecoder::new(payload);
50 let mut decompressed = Vec::new();
51 decoder.read_to_end(&mut decompressed).map_err(|err| {
52 Error::MalformedIndex(format!("zlib decompression of .rdb record failed: {err}"))
53 })?;
54 if decompressed.len() != declared_len {
55 return Err(Error::RecordSizeMismatch {
56 expected: declared_len,
57 actual: decompressed.len(),
58 });
59 }
60
61 Ok(rd_rds::parse(&decompressed)?)
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn maps_unknown_envelope_to_existing_error() {
70 let path = std::env::temp_dir().join(format!(
71 "rd-helpdb-unknown-envelope-{}.rds",
72 std::process::id()
73 ));
74 std::fs::write(&path, b"A\nunknown").expect("write unknown envelope");
75 let err = read_rds_file(&path).unwrap_err();
76 let _ = std::fs::remove_file(&path);
77 assert!(matches!(
78 err,
79 Error::UnsupportedCompression { magic, .. } if magic == b"A\nunkn"
80 ));
81 }
82
83 #[test]
84 fn rejects_short_records() {
85 let err = decode_rdb_record(&[0, 1, 2]).unwrap_err();
86 assert!(matches!(
87 err,
88 Error::RecordSizeMismatch {
89 expected: 4,
90 actual: 3
91 }
92 ));
93 }
94}