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(&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>( &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()); } } }