From 405ffd194f8b3e5c8beb133a22164d275dbd7fd0 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Thu, 23 Mar 2023 13:14:17 -0400 Subject: [PATCH] Add `Archive` struct to `tar` --- tar/src/lib.rs | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ tar/src/node.rs | 6 +-- 2 files changed, 110 insertions(+), 5 deletions(-) diff --git a/tar/src/lib.rs b/tar/src/lib.rs index 1a9362a..fea90c7 100644 --- a/tar/src/lib.rs +++ b/tar/src/lib.rs @@ -1,3 +1,5 @@ +use std::{fs::File, io::{self, BufReader}}; + mod error; mod header; mod node; @@ -10,3 +12,110 @@ pub use { pub struct Archive { pub nodes: Vec, } + +impl Archive { + /// Write out a vector of `TarNodes` to a file or something that implements ``std::io::Write`` and ``std::io::Copy``. + /// + /// # Example + /// + /// ``` + /// use std::fs::File; + /// use tar::tar::TarFile; + /// + /// let data = Archive::new("test/1.txt".to_string()).unwrap(); + /// + /// let out = File::create("test/2.tar".to_string()).unwrap(); + /// data.write(&out).unwrap(); + /// ``` + pub fn write(self, mut input: T) -> Result { + let mut written = 0; + for f in self.nodes.clone() { + written += f.write(input)?; + } + + /* Complete the write with 18 blocks of 512 ``0x00`` bytes per the specification */ + if !self.nodes.is_empty() { + input.write_all(&[0; 9216])?; + written += 9216; + } + + Ok(written) + } + + /// Create a new `TarFile` struct and initialize it with a `filename` file. This will read in the file to + /// the `TarFile` struct as a `TarNode`. + /// + /// # Example + /// + /// ``` + /// use tar::Archive; + /// + /// let data = Archive::new("test/1.txt".to_string()).unwrap(); + /// ``` + pub fn new(filename: String) -> Result { + Ok(Self { + nodes: vec![Node::read_file_to_tar(filename)?], + }) + } + + /// Append another file to the `TarFile.file` vector. This adds a file to the internal representation of the tar file. + /// + /// # Example + /// + /// ``` + /// use tar::Archive; + /// + /// let mut data = Archive::new("test/1.txt".to_string()).unwrap(); + /// data.append("test/1.txt".to_string()).unwrap(); + /// ``` + pub fn append(&mut self, filename: String) -> Result<(), Error> { + self.nodes.push(Node::read_file_to_tar(filename)?); + + Ok(()) + } + + /// Open and load an external tar file into the internal `TarFile` struct. This parses and loads up all the files + /// contained within the external tar file. + /// + /// # Example + /// + /// ``` + /// use tar::Archive; + /// + /// Archive::open("test/1.tar".to_string()).unwrap(); + /// ``` + pub fn open(filename: String) -> Result { + let file = File::open(&filename)?; + let mut reader = BufReader::new(file); + let mut out = Self { + nodes: Vec::::new(), + }; + + while let Ok(t) = Node::read(&mut reader) { + out.nodes.push(t); + } + + Ok(out) + } + + /// Remove the first file from the Tar that matches the filename and path. + /// + /// # Example + /// + /// ``` + /// use minitar::tar::TarFile; + /// + /// let mut data = TarFile::new("test/1.tar".to_string()).unwrap(); + /// data.remove("test/1.tar".to_string()).unwrap(); + /// ``` + pub fn remove(&mut self, filename: String) -> Result { + let mut name = [0u8; 100]; + name[..filename.len()].copy_from_slice(filename.as_bytes()); + if let Some(i) = &self.nodes.iter().position(|x| x.header.fname == name) { + self.nodes.remove(*i); + return Ok(true); + } + + Ok(false) + } +} diff --git a/tar/src/node.rs b/tar/src/node.rs index cd52b0d..06397fa 100644 --- a/tar/src/node.rs +++ b/tar/src/node.rs @@ -67,11 +67,7 @@ impl Node { } /// Create a Node from in memory data, given the filename and metadata - pub fn read_data_to_tar( - data: &[u8], - filename: &str, - meta: &Metadata, - ) -> Result { + pub fn read_data_to_tar(data: &[u8], filename: &str, meta: &Metadata) -> Result { let header = Header::new_from_meta(filename, meta)?; if header.link_indicator[0] != FileType::Normal as u8 { return Ok(Node {