Update the write API

This change adds a FileOptions struct. This struct can be filled using a builder pattern
to set the options for a file or folder.

With this change, we also introduce the option to set the (unix) permissions of a file or folder.
This commit is contained in:
Mathijs van de Nes 2016-10-30 09:09:50 +01:00
parent 82315c9511
commit d2d19f6539
3 changed files with 91 additions and 15 deletions

View file

@ -1,7 +1,7 @@
[package]
name = "zip"
version = "0.1.19"
version = "0.2.0"
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
license = "MIT"
repository = "https://github.com/mvdnes/zip-rs.git"

View file

@ -1,4 +1,5 @@
use std::io::prelude::*;
use zip::write::FileOptions;
extern crate zip;
@ -32,12 +33,13 @@ fn doit(filename: &str) -> zip::result::ZipResult<()>
let mut zip = zip::ZipWriter::new(file);
try!(zip.start_file("test/", zip::CompressionMethod::Stored));
try!(zip.add_directory("test/", FileOptions::default()));
try!(zip.start_file("test/☃.txt", zip::CompressionMethod::Stored));
let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored).unix_permissions(0o755);
try!(zip.start_file("test/☃.txt", options));
try!(zip.write_all(b"Hello, World!\n"));
try!(zip.start_file("test/lorem_ipsum.txt", zip::CompressionMethod::Deflated));
try!(zip.start_file("test/lorem_ipsum.txt", FileOptions::default()));
try!(zip.write_all(LOREM_IPSUM));
try!(zip.finish());

View file

@ -43,7 +43,8 @@ enum GenericZipWriter<W: Write + io::Seek>
/// let mut w = std::io::Cursor::new(buf);
/// let mut zip = zip::ZipWriter::new(w);
///
/// try!(zip.start_file("hello_world.txt", zip::CompressionMethod::Stored));
/// let options = zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored);
/// try!(zip.start_file("hello_world.txt", options));
/// try!(zip.write(b"Hello, World!"));
///
/// // Optionally finish the zip. (this is also done on drop)
@ -69,6 +70,50 @@ struct ZipWriterStats
bytes_written: u64,
}
/// Metadata for a file to be written
pub struct FileOptions {
compression_method: CompressionMethod,
last_modified_time: time::Tm,
permissions: Option<u32>,
}
impl FileOptions {
/// Construct a new FileOptions object
pub fn default() -> FileOptions {
FileOptions {
compression_method: CompressionMethod::Deflated,
last_modified_time: time::now(),
permissions: None,
}
}
/// Set the compression method for the new file
///
/// The default is `CompressionMethod::Deflated`
pub fn compression_method(mut self, method: CompressionMethod) -> FileOptions {
self.compression_method = method;
self
}
/// Set the last modified time
///
/// The default is the current timestamp
pub fn last_modified_time(mut self, mod_time: time::Tm) -> FileOptions {
self.last_modified_time = mod_time;
self
}
/// Set the permissions for the new file.
///
/// The format is represented with unix-style permissions.
/// The default is `0o644`, which represents `rw-r--r--` for files,
/// and `0o755`, which represents `rwxr-xr-x` for directories
pub fn unix_permissions(mut self, mode: u32) -> FileOptions {
self.permissions = Some(mode & 0o777);
self
}
}
impl<W: Write+io::Seek> Write for ZipWriter<W>
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
@ -122,8 +167,8 @@ impl<W: Write+io::Seek> ZipWriter<W>
}
}
/// Start a new file for with the requested compression method.
pub fn start_file<S>(&mut self, name: S, compression: CompressionMethod) -> ZipResult<()>
/// Start a new file for with the requested options.
fn start_entry<S>(&mut self, name: S, options: FileOptions) -> ZipResult<()>
where S: Into<String>
{
try!(self.finish_file());
@ -132,13 +177,14 @@ impl<W: Write+io::Seek> ZipWriter<W>
let writer = self.inner.get_plain();
let header_start = try!(writer.seek(io::SeekFrom::Current(0)));
let permissions = options.permissions.unwrap_or(0o100644);
let mut file = ZipFileData
{
system: System::Dos,
system: System::Unix,
version_made_by: DEFAULT_VERSION,
encrypted: false,
compression_method: compression,
last_modified_time: time::now(),
compression_method: options.compression_method,
last_modified_time: options.last_modified_time,
crc32: 0,
compressed_size: 0,
uncompressed_size: 0,
@ -146,7 +192,7 @@ impl<W: Write+io::Seek> ZipWriter<W>
file_comment: String::new(),
header_start: header_start,
data_start: 0,
external_attributes: 0,
external_attributes: permissions << 16,
};
try!(write_local_file_header(writer, &file));
@ -160,7 +206,7 @@ impl<W: Write+io::Seek> ZipWriter<W>
self.files.push(file);
}
try!(self.inner.switch_to(compression));
try!(self.inner.switch_to(options.compression_method));
Ok(())
}
@ -184,9 +230,36 @@ impl<W: Write+io::Seek> ZipWriter<W>
Ok(())
}
/// Starts a file.
pub fn start_file<S>(&mut self, name: S, mut options: FileOptions) -> ZipResult<()>
where S: Into<String>
{
if options.permissions.is_none() {
options.permissions = Some(0o644);
}
*options.permissions.as_mut().unwrap() |= 0o100000;
try!(self.start_entry(name, options));
Ok(())
}
/// Add a directory entry.
///
/// You should not write data to the file afterwards.
pub fn add_directory<S>(&mut self, name: S, mut options: FileOptions) -> ZipResult<()>
where S: Into<String>
{
if options.permissions.is_none() {
options.permissions = Some(0o755);
}
*options.permissions.as_mut().unwrap() |= 0o40000;
options.compression_method = CompressionMethod::Stored;
try!(self.start_entry(name, options));
Ok(())
}
/// Finish the last file and write all other zip-structures
///
/// This will return the writer, but one should normally not append any data to the end of the file.
/// This will return the writer, but one should normally not append any data to the end of the file.
/// Note that the zipfile will also be finished on drop.
pub fn finish(&mut self) -> ZipResult<W>
{
@ -369,7 +442,8 @@ fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData)
// central file header signature
try!(writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
// version made by
try!(writer.write_u16::<LittleEndian>(0x14FF));
let version_made_by = (file.system as u16) << 8 | (file.version_made_by as u16);
try!(writer.write_u16::<LittleEndian>(version_made_by));
// version needed to extract
try!(writer.write_u16::<LittleEndian>(20));
// general puprose bit flag
@ -399,7 +473,7 @@ fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData)
// internal file attribytes
try!(writer.write_u16::<LittleEndian>(0));
// external file attributes
try!(writer.write_u32::<LittleEndian>(0));
try!(writer.write_u32::<LittleEndian>(file.external_attributes));
// relative offset of local header
try!(writer.write_u32::<LittleEndian>(file.header_start as u32));
// file name