use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::Path;
use flate2::read::GzDecoder;
use crate::error::Result;
pub enum MaybeGzReader {
Plain(BufReader<File>),
Gzip(BufReader<GzDecoder<File>>),
}
impl Read for MaybeGzReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
MaybeGzReader::Plain(reader) => reader.read(buf),
MaybeGzReader::Gzip(reader) => reader.read(buf),
}
}
}
impl BufRead for MaybeGzReader {
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
match self {
MaybeGzReader::Plain(reader) => reader.fill_buf(),
MaybeGzReader::Gzip(reader) => reader.fill_buf(),
}
}
fn consume(&mut self, amt: usize) {
match self {
MaybeGzReader::Plain(reader) => reader.consume(amt),
MaybeGzReader::Gzip(reader) => reader.consume(amt),
}
}
}
pub fn is_gzip_path<P: AsRef<Path>>(path: P) -> bool {
path.as_ref()
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("gz"))
}
pub fn open_maybe_gz<P: AsRef<Path>>(path: P) -> Result<MaybeGzReader> {
let file = File::open(&path)?;
if is_gzip_path(&path) {
let decoder = GzDecoder::new(file);
Ok(MaybeGzReader::Gzip(BufReader::new(decoder)))
} else {
Ok(MaybeGzReader::Plain(BufReader::new(file)))
}
}
pub fn read_maybe_gz_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
let file = File::open(&path)?;
let mut content = String::new();
if is_gzip_path(&path) {
let mut decoder = GzDecoder::new(file);
decoder.read_to_string(&mut content)?;
} else {
let mut reader = BufReader::new(file);
reader.read_to_string(&mut content)?;
}
Ok(content)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_gzip_path() {
assert!(is_gzip_path("test.sdf.gz"));
assert!(is_gzip_path("test.mol2.GZ"));
assert!(is_gzip_path("test.xyz.Gz"));
assert!(!is_gzip_path("test.sdf"));
assert!(!is_gzip_path("test.mol2"));
assert!(!is_gzip_path("test.gz.sdf")); }
}