In the below code, I am copying the contents of a file present inside a tar to other files. After the first successful copy file position of the tar entry is now at the end, how do I reset the file position to the beginning after a successful copy operation. And, also how to reset archive position to the beginning?
use std::fs::File;
use std::path::Path;
use std::io::{Error, ErrorKind, Read, Seek, Write};
use std::ffi::OsStr;
use tar::{Archive, Entry};
use flate2::read::GzDecoder;
enum ArchiveType {
Tar,
TarGz,
}
fn get_archive(archive_type: ArchiveType, archive: &Path) -> Archive<Box<dyn Read>> {
let file = File::open(archive).expect(&format!("Failed to open archive {:?}", archive));
match archive_type {
ArchiveType::Tar => Archive::new(Box::new(file)),
ArchiveType::TarGz => Archive::new(Box::new(GzDecoder::new(file))),
}
}
fn get_entry<'a, R: Read>(
archive: &'a mut Archive<R>,
entry_name: &'a str,
) -> Result<Entry<'a, R>, Error> {
let normalized_entry_name = entry_name.trim_start_matches("./");
for entry in archive.entries_with_seek()? {
let entry = entry?;
// comparison between 'entry_name' and path of 'entry'
// is done only with file_name() we ignore subfolders.
// if multiple entries with same name, only first will be found.
if entry
.path()
.as_ref()
.ok() // Convert Result to Option<Cow<Path>> (ignoring errors)
.and_then(|path| path.file_name()) // Extract file name (Option<&OsStr>)
.and_then(OsStr::to_str) // Convert to Option<&str> safely
.map(|name| {
println!("{}: {}", name, normalized_entry_name);
name == normalized_entry_name}) // Compare
.unwrap_or(false)
{
return Ok(entry);
}
}
Err(Error::new(ErrorKind::Other, "failed to get entry from the archive"))
}
fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W) {
let mut buf = vec![0; 1024];
let mut bytes_read = buf.capacity();
while bytes_read > 0 {
bytes_read = reader.read(&mut buf).expect("failed to read from the file");
println!("bytesssss_readddd: {}", bytes_read);
if bytes_read > 0 {
writer.write_all(&buf[..bytes_read]).expect("failed to write to the file");
}
}
writer.flush().expect("failed to flush the writer");
}
fn main() {
let mut archive =
get_archive(ArchiveType::Tar, Path::new("ap.tar"));
let file_name = "main.rs".to_string();
{
let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
let dst_file = &File::create("b.txt").expect("unable to create the file");
write_to_file(&mut entry, dst_file);
}
{
let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
let dst_file = &File::create("c.txt").expect("unable to create the file");
write_to_file(&mut entry, dst_file);
}
}
Error
error[E0599]: the method `entries_with_seek` exists for mutable reference `&mut Archive<R>`, but its trait bounds were not satisfied
--> src/main.rs:27:26
|
27 | for entry in archive.entries_with_seek()? {
| ^^^^^^^^^^^^^^^^^ method cannot be called on `&mut Archive<R>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`R: Seek`
help: consider restricting the type parameter to satisfy the trait bound
|
25 | ) -> Result<Entry<'a, R>, Error> where R: Seek {
| +++++++++++++
For more information about this error, try `rustc --explain E0599`.
Cargo.toml
[dependencies]
tar = "0.4.44"
flate2 = "1.1.0"
You can't restart an Entry
it doesn't implement Seek
, so you'd recreate it from the Archive
, to be able to do that the underlying Read
er must implement Seek
and you have to use entries_with_seek
instead of the regular entries
(or manually seek to the start before calling entries
).
Since trait objects only implement the listed traits and their supertraits a Box<dyn Read>
is not sufficient to use with entries_with_seek
. You'll have to change it to either of the following:
If you want dynamic dispatch on the reader you could use a custom trait that has both Seek
and Read
as supertraits as explained in Can I get a trait object of a multi-trait instance without using a generic type?
Then you can use Box<dyn SeekRead>
as your seekable reader.
You can also avoid dynamic dispatch alltogether with an enum.