rieter/libarchive/src/write/file.rs

110 lines
2.9 KiB
Rust

use super::WriteEntry;
use crate::error::ArchiveError;
use crate::Entry;
use crate::Handle;
use libarchive3_sys::ffi;
use std::fs;
use std::io;
use std::io::Read;
use std::path::Path;
pub struct FileWriter {
handle: *mut ffi::Struct_archive,
closed: bool,
}
impl Handle for FileWriter {
unsafe fn handle(&self) -> *const ffi::Struct_archive {
self.handle as *const _
}
unsafe fn handle_mut(&mut self) -> *mut ffi::Struct_archive {
self.handle
}
}
impl FileWriter {
pub fn new(handle: *mut ffi::Struct_archive) -> Self {
FileWriter {
handle,
closed: false,
}
}
pub fn append_data<R: Read>(&mut self, entry: &mut WriteEntry, r: &mut R) -> crate::Result<()> {
unsafe {
match ffi::archive_write_header(self.handle_mut(), entry.entry_mut()) {
ffi::ARCHIVE_OK => (),
_ => return Err(ArchiveError::from(self as &dyn Handle).into()),
}
}
let mut buf = [0; 4096];
loop {
match r.read(&mut buf) {
Ok(0) => return Ok(()),
// Write entire buffer
Ok(buf_len) => {
let mut written: usize = 0;
while written < buf_len {
let res = unsafe {
ffi::archive_write_data(
self.handle_mut(),
&buf[written] as *const u8 as *const _,
buf_len - written,
)
} as isize;
// Negative values signal errors
if res < 0 {
return Err(ArchiveError::from(self as &dyn Handle).into());
}
written += usize::try_from(res).unwrap();
}
}
Err(err) => match err.kind() {
io::ErrorKind::Interrupted => (),
_ => return Err(err.into()),
},
};
}
}
pub fn append_path<P: AsRef<Path>>(
&mut self,
entry: &mut WriteEntry,
path: P,
) -> crate::Result<()> {
let mut f = fs::File::open(path)?;
self.append_data(entry, &mut f)
}
pub fn close(&mut self) -> crate::Result<()> {
unsafe {
match ffi::archive_write_close(self.handle_mut()) {
ffi::ARCHIVE_OK => {
self.closed = true;
Ok(())
}
_ => Err(ArchiveError::from(self as &dyn Handle)),
}
}
}
}
impl Drop for FileWriter {
fn drop(&mut self) {
if !self.closed {
let _ = self.close();
}
unsafe {
ffi::archive_write_free(self.handle_mut());
}
}
}