1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
use crate::archive::Archive;
use crate::error::ZipErrorT;
use crate::ffi;
use crate::Error;
use crate::Result;
use std::io;
use std::marker::PhantomData;
use std::ptr::null_mut;

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Encoding {
    Guess,
    Utf8,
    Cp437,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum OpenFlag {
    Compressed,
    Unchanged,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum LocateFlag {
    NoCase,
    NoDir,
    EncodingRaw,
    EncodingGuess,
    EncodingStrict,
}

#[derive(Debug)]
pub struct File<'a> {
    pub(crate) handle: *mut ffi::zip_file_t,
    pub(crate) phantom: PhantomData<&'a Archive>,
}

impl File<'_> {
    fn error(&mut self) -> ZipErrorT<&mut ffi::zip_error_t> {
        unsafe {
            let error = ffi::zip_file_get_error(self.handle);
            (&mut *error).into()
        }
    }

    pub fn close(mut self) -> Result<()> {
        if self.handle.is_null() {
            Ok(())
        } else {
            let result = unsafe { ffi::zip_fclose(self.handle) };
            self.handle = null_mut();
            if result == 0 {
                Ok(())
            } else {
                let error: ZipErrorT<_> = result.into();
                Err(error.into())
            }
        }
    }
}
impl Drop for File<'_> {
    fn drop(&mut self) {
        if !self.handle.is_null() {
            unsafe {
                ffi::zip_fclose(self.handle);
            }
        }
    }
}

impl io::Read for File<'_> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let result = unsafe { ffi::zip_fread(self.handle, buf.as_mut_ptr() as _, buf.len() as _) };
        if result == -1 {
            let error: Error = self.error().into();
            Err(io::Error::new(io::ErrorKind::Other, error))
        } else {
            Ok(result as _)
        }
    }
}

impl io::Seek for File<'_> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        let result = unsafe {
            match pos {
                io::SeekFrom::Start(pos) => {
                    ffi::zip_fseek(self.handle, pos as _, ffi::SEEK_SET as _)
                }
                io::SeekFrom::End(pos) => ffi::zip_fseek(self.handle, pos as _, ffi::SEEK_END as _),
                io::SeekFrom::Current(pos) => {
                    ffi::zip_fseek(self.handle, pos as _, ffi::SEEK_CUR as _)
                }
            }
        };
        if result == -1 {
            let error: Error = self.error().into();
            Err(io::Error::new(io::ErrorKind::Other, error))
        } else {
            unsafe {
                // Assume this will work, otherwise the fseek would have already failed.
                Ok(ffi::zip_ftell(self.handle) as _)
            }
        }
    }
}