use flate2::read::GzDecoder;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
use std::path::Path;
use digest::{self, Digest, DigestData};
use errors::{Error, ErrorKind, Result};
use status::StatusBackend;
pub mod filesystem;
pub mod genuine_stdout;
pub mod itarbundle;
pub mod local_cache;
pub mod memory;
pub mod stack;
pub mod zipbundle;
pub trait InputFeatures: Read {
fn get_size(&mut self) -> Result<usize>;
fn try_seek(&mut self, pos: SeekFrom) -> Result<u64>;
}
pub struct InputHandle {
name: OsString,
inner: Box<InputFeatures>,
digest: digest::DigestComputer,
ever_read: bool,
did_unhandled_seek: bool,
}
impl InputHandle {
pub fn new<T: 'static + InputFeatures>(name: &OsStr, inner: T) -> InputHandle {
InputHandle {
name: name.to_os_string(),
inner: Box::new(inner),
digest: digest::create(),
ever_read: false,
did_unhandled_seek: false,
}
}
pub fn name(&self) -> &OsStr {
self.name.as_os_str()
}
pub fn into_name_digest(self) -> (OsString, Option<DigestData>) {
if self.did_unhandled_seek || !self.ever_read {
(self.name, None)
} else {
(self.name, Some(DigestData::from(self.digest)))
}
}
}
impl Read for InputHandle {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.ever_read = true;
let n = self.inner.read(buf)?;
self.digest.input(&buf[..n]);
Ok(n)
}
}
impl InputFeatures for InputHandle {
fn get_size(&mut self) -> Result<usize> {
self.inner.get_size()
}
fn try_seek(&mut self, pos: SeekFrom) -> Result<u64> {
match pos {
SeekFrom::Start(0) => {
self.digest.reset();
self.ever_read = false;
}
SeekFrom::Current(0) => {
},
_ => {
self.did_unhandled_seek = true;
}
}
self.inner.try_seek(pos)
}
}
pub struct OutputHandle {
name: OsString,
inner: Box<Write>,
digest: digest::DigestComputer,
}
impl OutputHandle {
pub fn new<T: 'static + Write>(name: &OsStr, inner: T) -> OutputHandle {
OutputHandle {
name: name.to_os_string(),
inner: Box::new(inner),
digest: digest::create(),
}
}
pub fn name(&self) -> &OsStr {
self.name.as_os_str()
}
pub fn into_name_digest(self) -> (OsString, DigestData) {
(self.name, DigestData::from(self.digest))
}
}
impl Write for OutputHandle {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.digest.input(buf);
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
pub enum OpenResult<T> {
Ok(T),
NotAvailable,
Err(Error)
}
pub trait IoProvider {
fn output_open_name(&mut self, _name: &OsStr) -> OpenResult<OutputHandle> {
OpenResult::NotAvailable
}
fn output_open_stdout(&mut self) -> OpenResult<OutputHandle> {
OpenResult::NotAvailable
}
fn input_open_name(&mut self, _name: &OsStr, _status: &mut StatusBackend) -> OpenResult<InputHandle> {
OpenResult::NotAvailable
}
}
impl<R: Read> InputFeatures for GzDecoder<R> {
fn get_size(&mut self) -> Result<usize> {
Err(ErrorKind::NotSizeable.into())
}
fn try_seek(&mut self, _: SeekFrom) -> Result<u64> {
Err(ErrorKind::NotSeekable.into())
}
}
impl InputFeatures for Cursor<Vec<u8>> {
fn get_size(&mut self) -> Result<usize> {
Ok(self.get_ref().len())
}
fn try_seek(&mut self, pos: SeekFrom) -> Result<u64> {
Ok(self.seek(pos)?)
}
}
pub use self::filesystem::FilesystemIo;
pub use self::genuine_stdout::GenuineStdoutIo;
pub use self::memory::MemoryIo;
pub use self::stack::IoStack;
pub fn try_open_file(path: &Path) -> OpenResult<File> {
use std::io::ErrorKind::NotFound;
match File::open(path) {
Ok(f) => OpenResult::Ok(f),
Err(e) => {
if e.kind() == NotFound {
OpenResult::NotAvailable
} else {
OpenResult::Err(e.into())
}
},
}
}
pub mod testing {
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::path::{Path, PathBuf};
use super::*;
pub struct SingleInputFileIo {
name: OsString,
full_path: PathBuf
}
impl SingleInputFileIo {
pub fn new(path: &Path) -> SingleInputFileIo {
let p = path.to_path_buf();
SingleInputFileIo {
name: p.file_name().unwrap().to_os_string(),
full_path: p,
}
}
}
impl IoProvider for SingleInputFileIo {
fn output_open_name(&mut self, _: &OsStr) -> OpenResult<OutputHandle> {
OpenResult::NotAvailable
}
fn output_open_stdout(&mut self) -> OpenResult<OutputHandle> {
OpenResult::NotAvailable
}
fn input_open_name(&mut self, name: &OsStr, _status: &mut StatusBackend) -> OpenResult<InputHandle> {
if name == self.name {
OpenResult::Ok(InputHandle::new(name, File::open(&self.full_path).unwrap()))
} else {
OpenResult::NotAvailable
}
}
}
}