use std::ffi::OsStr;
use std::fs::File;
use std::path::Path;
#[allow(dead_code)]
mod tool;
#[cfg(feature = "gzip")]
mod gz {
use std::fs::File;
use std::io::Result;
use std::path::Path;
type Read = flate2::read::GzDecoder<File>;
type Write = flate2::write::GzEncoder<File>;
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
File::open(path).map(Read::new)
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
File::create(path).map(|f| Write::new(f, flate2::Compression::default()))
}
}
#[cfg(not(feature = "gzip"))]
mod gz {
use std::ffi::OsStr;
use std::io::Result;
use std::path::Path;
type Read = crate::tool::ToolRead;
type Write = crate::tool::ToolWrite;
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
Read::new(
"/usr/bin/gzip",
[OsStr::new("-d"), OsStr::new("-c"), path.as_ref().as_os_str()],
)
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
Write::new_with_file("/usr/bin/gzip", [OsStr::new("-")], path.as_ref().as_os_str())
}
}
#[cfg(feature = "bzip2")]
mod bzip {
use std::fs::File;
use std::io::Result;
use std::path::Path;
type Read = bzip2::read::BzDecoder<File>;
type Write = bzip2::write::BzEncoder<File>;
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
Ok(Read::new(File::open(path)?))
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
Ok(Write::new(File::create(path)?, bzip2::Compression::default()))
}
}
#[cfg(not(feature = "bzip2"))]
mod bzip {
use std::ffi::OsStr;
use std::io::Result;
use std::path::Path;
type Read = crate::tool::ToolRead;
type Write = crate::tool::ToolWrite;
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
Read::new(
"/usr/bin/bzip2",
[OsStr::new("-d"), OsStr::new("-c"), path.as_ref().as_os_str()],
)
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
Write::new_with_file("/usr/bin/bzip2", [OsStr::new("-")], path.as_ref().as_os_str())
}
}
#[cfg(feature = "xz")]
mod xz {
use std::fmt::Arguments;
use std::fs::File;
use std::io::{self, Error, ErrorKind, Result};
use std::path::Path;
use std::result;
type Read = lzma::LzmaReader<File>;
pub struct Write(Option<lzma::LzmaWriter<File>>);
impl Write {
fn new(path: impl AsRef<Path>) -> result::Result<Self, lzma::error::LzmaError> {
Ok(Write(Some(lzma::LzmaWriter::new_compressor(File::create(path)?, 6)?)))
}
}
impl io::Write for Write {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.0.as_mut().unwrap().write(buf)
}
fn flush(&mut self) -> Result<()> {
self.0.as_mut().unwrap().flush()
}
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
self.0.as_mut().unwrap().write_all(buf)
}
fn write_fmt(&mut self, fmt: Arguments) -> Result<()> {
self.0.as_mut().unwrap().write_fmt(fmt)
}
}
impl Drop for Write {
fn drop(&mut self) {
self.0.take().unwrap().finish().expect("Finish XZ stream");
}
}
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
match Read::new_decompressor(File::open(path)?) {
Ok(r) => Ok(r),
Err(lzma::error::LzmaError::Io(e)) => Err(e),
Err(e) => Err(Error::new(ErrorKind::Other, e)),
}
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
match Write::new(path) {
Ok(r) => Ok(r),
Err(lzma::error::LzmaError::Io(e)) => Err(e),
Err(e) => Err(Error::new(ErrorKind::Other, e)),
}
}
}
#[cfg(not(feature = "xz"))]
mod xz {
use std::ffi::OsStr;
use std::io::Result;
use std::path::Path;
type Read = crate::tool::ToolRead;
type Write = crate::tool::ToolWrite;
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
Read::new(
"/usr/bin/xz",
[OsStr::new("-d"), OsStr::new("-c"), path.as_ref().as_os_str()],
)
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
Write::new_with_file("/usr/bin/xz", [OsStr::new("-")], path.as_ref().as_os_str())
}
}
#[cfg(feature = "zstd")]
mod zstd {
use std::fs::File;
use std::io::{BufReader, Result};
use std::path::Path;
type Read = zstd::stream::Decoder<'static, BufReader<File>>;
type Write = zstd::stream::AutoFinishEncoder<'static, File>;
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
zstd::stream::Decoder::new(File::open(path)?)
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
zstd::stream::Encoder::new(File::create(path)?, 0).map(|e| e.auto_finish())
}
}
#[cfg(not(feature = "zstd"))]
mod zstd {
use std::ffi::OsStr;
use std::io::Result;
use std::path::Path;
type Read = crate::tool::ToolRead;
type Write = crate::tool::ToolWrite;
pub fn read(path: impl AsRef<Path>) -> Result<Read> {
Read::new(
"/usr/bin/zstd",
[OsStr::new("-d"), OsStr::new("-c"), path.as_ref().as_os_str()],
)
}
pub fn write(path: impl AsRef<Path>) -> Result<Write> {
Write::new_with_file("/usr/bin/zstd", [OsStr::new("-")], path.as_ref().as_os_str())
}
}
pub fn read(path: impl AsRef<Path>) -> std::io::Result<Box<dyn std::io::Read>> {
let ext = path.as_ref().extension().unwrap_or_else(|| OsStr::new(""));
Ok(if ext == "gz" {
Box::new(gz::read(path)?)
} else if ext == "bz2" {
Box::new(bzip::read(path)?)
} else if ext == "xz" || ext == "lzma" {
Box::new(xz::read(path)?)
} else if ext == "zst" {
Box::new(zstd::read(path)?)
} else {
Box::new(File::open(path)?)
})
}
pub fn write(path: impl AsRef<Path>) -> std::io::Result<Box<dyn std::io::Write>> {
let ext = path.as_ref().extension().unwrap_or_else(|| OsStr::new(""));
Ok(if ext == "gz" {
Box::new(gz::write(path)?)
} else if ext == "bz2" {
Box::new(bzip::write(path)?)
} else if ext == "xz" || ext == "lzma" {
Box::new(xz::write(path)?)
} else if ext == "zst" {
Box::new(zstd::write(path)?)
} else {
Box::new(File::create(path)?)
})
}
#[test]
fn test_write_and_read() {
let test_str = "Hello World!\n";
for &ext in &["", ".gz", ".bz2", ".lzma", ".xz", ".zst"] {
let mut dir = std::env::temp_dir();
dir.push(format!("__zopen-rs-test__{}", ext));
{
let mut f = write(dir.to_str().unwrap()).unwrap();
write!(f, "{}", test_str).unwrap();
f.flush().unwrap();
}
assert!(dir.exists());
{
let mut f = read(dir.to_str().unwrap()).unwrap();
let mut data = "".to_string();
assert_eq!(f.read_to_string(&mut data).unwrap(), test_str.len());
assert_eq!(data, test_str);
}
std::fs::remove_file(dir).unwrap();
}
}