rust/physfs-rs/src/physfs/file.rs
changeset 14456 a1613788130d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/physfs-rs/src/physfs/file.rs	Thu Dec 13 23:44:46 2018 +0100
@@ -0,0 +1,216 @@
+use std::ffi::CString;
+use std::io::{ Read, Write, Seek, SeekFrom, Result };
+use std::mem;
+use libc::{ c_int, c_char, c_void };
+use primitives::*;
+use super::{ PhysFSContext };
+use super::util::physfs_error_as_io_error;
+
+#[link(name = "physfs")]
+extern {
+    // valid filehandle on success, NULL on failure
+    fn PHYSFS_openAppend(filename: *const c_char) -> *const RawFile;
+    fn PHYSFS_openRead(filename: *const c_char) -> *const RawFile;
+    fn PHYSFS_openWrite(filename: *const c_char) -> *const RawFile;
+
+    // nonzero on success, 0 on failure (and the handle stays open)
+    // The docs make it sound like failure is rare.
+    fn PHYSFS_close(file: *const RawFile) -> c_int;
+
+    // Number of bytes read on success, -1 on failure.
+    fn PHYSFS_read(file: *const RawFile, buffer: *mut c_void,
+                   obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64;
+
+    // Number of bytes written on success, -1 on failure.
+    fn PHYSFS_write(file: *const RawFile, buffer: *const c_void,
+                    obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64;
+
+    // Flush buffered file; no-op for unbuffered files.
+    fn PHYSFS_flush(file: *const RawFile) -> c_int;
+
+    // Seek to position in file; nonzero on succss, zero on error.
+    fn PHYSFS_seek(file: *const RawFile, pos: PHYSFS_uint64) -> c_int;
+
+    // Current position in file, -1 on failure.
+    fn PHYSFS_tell(file: *const RawFile) -> PHYSFS_sint64;
+
+    // nonzero if EOF, zero if not.
+    fn PHYSFS_eof(file: *const RawFile) -> c_int;
+
+    // Determine file size; returns -1 if impossible
+    fn PHYSFS_fileLength(file: *const RawFile) -> PHYSFS_sint64;
+}
+
+/// Possible ways to open a file.
+#[derive(Copy, Clone)]
+pub enum Mode {
+    /// Append to the end of the file.
+    Append,
+    /// Read from the file.
+    Read,
+    /// Write to the file, overwriting previous data.
+    Write,
+}
+
+/// A wrapper for the PHYSFS_File type.
+#[repr(C)]
+struct RawFile {
+    opaque: *const c_void,
+}
+
+/// A file handle.
+#[allow(dead_code)]
+pub struct File<'f> {
+    raw: *const RawFile,
+    mode: Mode,
+    context: &'f PhysFSContext,
+}
+
+impl<'f> File<'f> {
+    /// Opens a file with a specific mode.
+    pub fn open<'g>(context: &'g PhysFSContext, filename: String, mode: Mode) -> Result<File<'g>> {
+        let c_filename = try!(CString::new(filename));
+        let raw = unsafe { match mode {
+            Mode::Append => PHYSFS_openAppend(c_filename.as_ptr()),
+            Mode::Read => PHYSFS_openRead(c_filename.as_ptr()),
+            Mode::Write => PHYSFS_openWrite(c_filename.as_ptr())
+        }};
+
+        if raw.is_null() {
+            Err(physfs_error_as_io_error())
+        }
+        else {
+            Ok(File{raw: raw, mode: mode, context: context})
+        }
+    }
+
+    /// Closes a file handle.
+    fn close(&self) -> Result<()> {
+        match unsafe {
+            PHYSFS_close(self.raw)
+        } {
+            0 => Err(physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+
+    /// Checks whether eof is reached or not.
+    pub fn eof(&self) -> bool {
+        let ret = unsafe {
+            PHYSFS_eof(self.raw)
+        };
+
+        ret != 0
+    }
+
+    /// Determine length of file, if possible
+    pub fn len(&self) -> Result<u64> {
+        let len = unsafe { PHYSFS_fileLength(self.raw) };
+
+        if len >= 0 {
+            Ok(len as u64)
+        } else {
+            Err(physfs_error_as_io_error())
+        }
+    }
+
+    /// Determines current position within a file
+    pub fn tell(&self) -> Result<u64> {
+        let ret = unsafe {
+            PHYSFS_tell(self.raw)
+        };
+
+        match ret {
+            -1 => Err(physfs_error_as_io_error()),
+            _ => Ok(ret as u64)
+        }
+    }
+}
+
+impl<'f> Read for File<'f> {
+    /// Reads from a file
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+        let ret = unsafe {
+            PHYSFS_read(
+                self.raw,
+                buf.as_ptr() as *mut c_void,
+                mem::size_of::<u8>() as PHYSFS_uint32,
+                buf.len() as PHYSFS_uint32
+            )
+        };
+
+        match ret {
+            -1 => Err(physfs_error_as_io_error()),
+            _ => Ok(ret as usize)
+        }
+    }
+}
+
+impl<'f> Write for File<'f> {
+    /// Writes to a file.
+    /// This code performs no safety checks to ensure
+    /// that the buffer is the correct length.
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        let ret = unsafe {
+            PHYSFS_write(
+                self.raw,
+                buf.as_ptr() as *const c_void,
+                mem::size_of::<u8>() as PHYSFS_uint32,
+                buf.len() as PHYSFS_uint32
+            )
+        };
+
+        match ret {
+            -1 => Err(physfs_error_as_io_error()),
+            _ => Ok(ret as usize)
+        }
+    }
+
+    /// Flushes a file if buffered; no-op if unbuffered.
+    fn flush(&mut self) -> Result<()> {
+        let ret = unsafe {
+            PHYSFS_flush(self.raw)
+        };
+
+        match ret {
+            0 => Err(physfs_error_as_io_error()),
+            _ => Ok(())
+        }
+    }
+}
+
+impl<'f> Seek for File<'f> {
+    /// Seek to a new position within a file
+    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
+        let seek_pos = match pos {
+            SeekFrom::Start(n) => n as i64,
+            SeekFrom::End(n) => {
+                let len = try!(self.len());
+                n + len as i64
+            }
+            SeekFrom::Current(n) => {
+                let curr_pos = try!(self.tell());
+                n + curr_pos as i64
+            }
+        };
+
+        let result = unsafe {
+            PHYSFS_seek(
+                self.raw,
+                seek_pos as PHYSFS_uint64
+            )
+        };
+
+        if result == -1 {
+            return Err(physfs_error_as_io_error());
+        }
+
+        self.tell()
+    }
+}
+
+impl<'f> Drop for File<'f> {
+    fn drop(&mut self) {
+        let _ = self.close();
+    }
+}