datasus_dbc/
decompress.rs

1use super::error::{Error, Result};
2use explode::ExplodeReader;
3use std::fs::File;
4use std::fs::OpenOptions;
5use std::io;
6use std::io::Chain;
7use std::io::Cursor;
8use std::io::Read;
9use std::path::Path;
10
11type DbfReader<R> = Chain<Chain<Cursor<[u8; 10]>, Cursor<Vec<u8>>>, ExplodeReader<R>>;
12
13/// Decompress a .dbc file into a .dbf file
14pub fn decompress<P>(dbc_path: P, dbf_path: P) -> Result<()>
15where
16    P: AsRef<Path>,
17{
18    let dbc_file = File::open(dbc_path)?;
19    let mut dbf_reader = into_dbf_reader(dbc_file)?;
20    let mut dbf_file = OpenOptions::new().write(true).create(true).open(dbf_path)?;
21    io::copy(&mut dbf_reader, &mut dbf_file)?;
22
23    Ok(())
24}
25
26/// Transform a .dbc reader into a .dbf reader. Make sure `dbc_reader` starts at the beginning of the file.
27pub fn into_dbf_reader<R>(mut dbc_reader: R) -> Result<DbfReader<R>>
28where
29    R: io::Read,
30{
31    let mut pre_header: [u8; 10] = Default::default();
32    let mut crc32: [u8; 4] = Default::default();
33    dbc_reader
34        .read_exact(&mut pre_header)
35        .map_err(|_| Error::MissingHeader)?;
36
37    let header_size: usize = usize::from(pre_header[8]) + (usize::from(pre_header[9]) << 8);
38
39    let mut header: Vec<u8> = vec![0; header_size - 10];
40    dbc_reader
41        .read_exact(&mut header)
42        .map_err(|_| Error::InvalidHeaderSize)?;
43    dbc_reader
44        .read_exact(&mut crc32)
45        .map_err(|_| Error::InvalidHeaderSize)?;
46
47    // Create readers for each part of the file
48    let pre_header_reader = Cursor::new(pre_header);
49    let header_reader = Cursor::new(header);
50    let compressed_content_reader = ExplodeReader::new(dbc_reader);
51
52    let dbf_reader = pre_header_reader
53        .chain(header_reader)
54        .chain(compressed_content_reader);
55
56    Ok(dbf_reader)
57}
58
59#[cfg(test)]
60mod tests {
61    use std::fs;
62
63    use super::*;
64
65    #[test]
66    fn test_decompress() -> Result<()> {
67        let input = r"test\data\sids.dbc";
68        let output = r"test\data\sids.dbf";
69        let expected = r"test\data\expected-sids.dbf";
70
71        decompress(input, output)?;
72
73        let output_file = fs::read(output)?;
74        let expected_file = fs::read(expected)?;
75        fs::remove_file(output)?;
76
77        assert_eq!(
78            output_file, expected_file,
79            "Decompressed .dbf is not equal to expected result"
80        );
81
82        Ok(())
83    }
84}