Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: add delete/update examples #56

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions examples/delete_file_from_archive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// See this dicussion for further background on why it is done like this:
// https://github.com/zip-rs/zip/discussions/430

use zip::result::{ZipError, ZipResult};

fn main() {
std::process::exit(real_main());
}

fn real_main() -> i32 {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
println!(
"Usage: {} <filename> <file_within_archive_to_delete>",
args[0]
);
return 1;
}
let filename = &*args[1];
let file_to_remove = &*args[2];
match remove_file(filename, file_to_remove, false) {
Ok(_) => println!("{file_to_remove} deleted from {filename}"),
Err(e) => {
eprintln!("Error: {e:?}");
return 1;
}
}
0
}

fn remove_file(archive_filename: &str, file_to_remove: &str, in_place: bool) -> ZipResult<()> {
let fname = std::path::Path::new(archive_filename);
let zipfile = std::fs::File::open(fname)?;

let mut archive = zip::ZipArchive::new(zipfile)?;

// Open a new, empty archive for writing to
let new_filename = replacement_filename(archive_filename.as_ref())?;
let new_file = std::fs::File::create(&new_filename)?;
let mut new_archive = zip::ZipWriter::new(new_file);

// Loop through the original archive:
// - Skip the target file
// - Copy everything else across as raw, which saves the bother of decoding it
// The end effect is to have a new archive, which is a clone of the original,
// save for the target file which has been omitted i.e. deleted
let target: &std::path::Path = file_to_remove.as_ref();
for i in 0..archive.len() {
let file = archive.by_index_raw(i).unwrap();
match file.enclosed_name() {
Some(p) if p == target => (),
_ => new_archive.raw_copy_file(file)?,
}
}
new_archive.finish()?;

drop(archive);
drop(new_archive);

Check failure on line 58 in examples/delete_file_from_archive.rs

View workflow job for this annotation

GitHub Actions / Build and test --no-default-features: ubuntu-latest, msrv

use of moved value: `new_archive`

Check failure on line 58 in examples/delete_file_from_archive.rs

View workflow job for this annotation

GitHub Actions / style_and_docs (--no-default-features)

use of moved value: `new_archive`

Check failure on line 58 in examples/delete_file_from_archive.rs

View workflow job for this annotation

GitHub Actions / Build and test --no-default-features: macOS-latest, msrv

use of moved value: `new_archive`

Check failure on line 58 in examples/delete_file_from_archive.rs

View workflow job for this annotation

GitHub Actions / Build and test --no-default-features: macOS-latest, nightly

use of moved value: `new_archive`

// If we're doing this in place then overwrite the original with the new
if in_place {
std::fs::rename(new_filename, archive_filename)?;
}

Ok(())
}

fn replacement_filename(source: &std::path::Path) -> ZipResult<std::path::PathBuf> {
let mut new = std::path::PathBuf::from(source);
let mut stem = source.file_stem().ok_or(ZipError::FileNotFound)?.to_owned();
stem.push("_updated");
new.set_file_name(stem);
let ext = source.extension().ok_or(ZipError::FileNotFound)?;
new.set_extension(ext);
Ok(new)
}
83 changes: 83 additions & 0 deletions examples/update_file_in_archive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// See this dicussion for further background on why it is done like this:
// https://github.com/zip-rs/zip/discussions/430

use std::io::prelude::*;

use zip::result::{ZipError, ZipResult};

fn main() {
std::process::exit(real_main());
}

fn real_main() -> i32 {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
println!(
"Usage: {} <filename> <file_within_archive_to_update>",
args[0]
);
return 1;
}
let filename = &*args[1];
let file_to_update = &*args[2];
match update_file(filename, file_to_update, false) {
Ok(_) => println!("{file_to_update} updated in {filename}"),
Err(e) => {
eprintln!("Error: {e:?}");
return 1;
}
}
0
}

fn update_file(archive_filename: &str, file_to_update: &str, in_place: bool) -> ZipResult<()> {
let fname = std::path::Path::new(archive_filename);
let zipfile = std::fs::File::open(fname)?;

let mut archive = zip::ZipArchive::new(zipfile)?;

// Open a new, empty archive for writing to
let new_filename = replacement_filename(archive_filename.as_ref())?;
let new_file = std::fs::File::create(&new_filename)?;
let mut new_archive = zip::ZipWriter::new(new_file);

// Loop through the original archive:
// - Write the target file from some bytes
// - Copy everything else across as raw, which saves the bother of decoding it
// The end effect is to have a new archive, which is a clone of the original,
// save for the target file which has been re-written
let target: &std::path::Path = file_to_update.as_ref();
let new = b"Lorem ipsum";
for i in 0..archive.len() {
let file = archive.by_index_raw(i).unwrap();
match file.enclosed_name() {
Some(p) if p == target => {
new_archive.start_file(file_to_update, zip::write::FileOptions::default())?;

Check failure on line 55 in examples/update_file_in_archive.rs

View workflow job for this annotation

GitHub Actions / Build and test --no-default-features: ubuntu-latest, msrv

type annotations needed

Check failure on line 55 in examples/update_file_in_archive.rs

View workflow job for this annotation

GitHub Actions / style_and_docs (--no-default-features)

type annotations needed

Check failure on line 55 in examples/update_file_in_archive.rs

View workflow job for this annotation

GitHub Actions / Build and test --no-default-features: macOS-latest, msrv

type annotations needed

Check failure on line 55 in examples/update_file_in_archive.rs

View workflow job for this annotation

GitHub Actions / Build and test --no-default-features: macOS-latest, nightly

type annotations needed
new_archive.write_all(new)?;
new_archive.flush()?;
}
_ => new_archive.raw_copy_file(file)?,
}
}
new_archive.finish()?;

drop(archive);
drop(new_archive);

// If we're doing this in place then overwrite the original with the new
if in_place {
std::fs::rename(new_filename, archive_filename)?;
}

Ok(())
}

fn replacement_filename(source: &std::path::Path) -> ZipResult<std::path::PathBuf> {
let mut new = std::path::PathBuf::from(source);
let mut stem = source.file_stem().ok_or(ZipError::FileNotFound)?.to_owned();
stem.push("_updated");
new.set_file_name(stem);
let ext = source.extension().ok_or(ZipError::FileNotFound)?;
new.set_extension(ext);
Ok(new)
}
Loading