From 32b4f80715faef74d98e20875869de75262db1cc Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sat, 8 Apr 2023 19:10:30 -0400 Subject: [PATCH] Fix some issues with getting tar header fields; Add some doc tests in tar module; Add some tests for version checks; --- src/tar/header.rs | 142 +++++++++++++++++++++++++++---------------- src/version/rapid.rs | 26 ++++++++ test/2.tar | Bin 10240 -> 10240 bytes 3 files changed, 117 insertions(+), 51 deletions(-) diff --git a/src/tar/header.rs b/src/tar/header.rs index 6e22b78..6c669c2 100644 --- a/src/tar/header.rs +++ b/src/tar/header.rs @@ -117,25 +117,45 @@ impl Default for Header { } impl Header { + /// Gets the filename of this archive member + /// + /// # Example + /// ``` + /// use hpk_package::tar::Header; + /// + /// let header = Header::new("test/1.txt").unwrap(); + /// let filename = header.filename().unwrap(); + /// assert_eq!(filename.as_str(), "1.txt"); + /// ``` pub fn filename(&self) -> Result { let mut s = String::new(); for c in self.fname { - if c != b'\0' { - write!(s, "{c}")?; - } else { + if c == 0 { break; + } else { + write!(s, "{}", char::from(c))?; } } Ok(s) } + /// Gets the Unix mode of this archive member + /// + /// # Example + /// ``` + /// use hpk_package::tar::Header; + /// + /// let header = Header::new("test/1.txt").unwrap(); + /// let mode = header.mode().unwrap(); + /// assert_eq!(mode, 420); + /// ``` pub fn mode(&self) -> Result { let mut s = String::new(); for c in self.mode { - if c != b'\0' { - write!(s, "{c}")?; - } else { + if c == 0 { break; + } else { + write!(s, "{}", char::from(c))?; } } let mode = u32::from_str_radix(&s, 8)?; @@ -145,10 +165,10 @@ impl Header { fn uid(&self) -> Result { let mut s = String::new(); for c in self.mode { - if c != b'\0' { - write!(s, "{c}")?; - } else { + if c == 0 { break; + } else { + write!(s, "{}", char::from(c))?; } } let uid = u32::from_str_radix(&s, 8)?; @@ -158,10 +178,10 @@ impl Header { fn gid(&self) -> Result { let mut s = String::new(); for c in self.mode { - if c != b'\0' { - write!(s, "{c}")?; - } else { + if c == 0 { break; + } else { + write!(s, "{}", char::from(c))?; } } let gid = u32::from_str_radix(&s, 8)?; @@ -171,10 +191,10 @@ impl Header { fn username(&self) -> Result { let mut s = String::new(); for c in self.username { - if c != b'\0' { - write!(s, "{c}")?; - } else { + if c == 0 { break; + } else { + write!(s, "{}", char::from(c))?; } } Ok(s) @@ -183,10 +203,10 @@ impl Header { fn groupname(&self) -> Result { let mut s = String::new(); for c in self.groupname { - if c != b'\0' { - write!(s, "{c}")?; - } else { + if c == 0 { break; + } else { + write!(s, "{}", char::from(c))?; } } Ok(s) @@ -205,25 +225,61 @@ impl Header { }) } - pub fn prefix(&self) -> Result { + /// Gets the path to the file minus it's final component + /// + /// # Example + /// ``` + /// use hpk_package::tar::Header; + /// + /// let header = Header::new("test/1.txt").unwrap(); + /// let prefix = header.prefix().unwrap(); + /// assert_eq!(prefix.as_str(), "test"); + /// ``` + pub fn prefix(&self) -> Option { let mut s = String::new(); for c in self.file_prefix { - if c != b'\0' { - write!(s, "{c}")?; + if c != 0 { + write!(s, "{}", char::from(c)).ok()?; } else { break; } } - Ok(s) + if s.is_empty() { + None + } else { + Some(s) + } + } + + /// Gets the full file path to this archive member. + /// + /// # Example + /// + /// ``` + /// use hpk_package::tar::Header; + /// use std::path::PathBuf; + /// + /// let header = Header::new("test/1.txt").unwrap(); + /// let path = header.file_path().unwrap(); + /// assert_eq!(PathBuf::from("test/1.txt"), path); + /// ``` + pub fn file_path(&self) -> Result { + let mut path = match self.prefix() { + Some(p) => PathBuf::from(&p), + None => PathBuf::new(), + }; + let name = self.filename()?; + path.push(&name); + Ok(path) } pub fn new(filename: &str) -> Result { let mut header = Header::default(); let meta = fs::symlink_metadata(filename)?; - let (filename, prefix) = if filename.len() < 100 { - (filename.to_string(), None) - } else { - // Deal with file names longer than 100 bytes + let (filename, prefix) = { + // Original tar has a maximum file name length of 100 bytes. The ustar + // revision allows storing the path prefix separately, with 100 bytes + // reserved for the file name and 150 bytes for the rest of the path. let path = PathBuf::from(&filename); let name = match path.file_name().and_then(|n| n.to_str()) { Some(n) => n.to_string(), @@ -234,16 +290,8 @@ impl Header { ))) } }; - let dir = match path.parent() { - Some(d) => d, - None => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "Cannot get path prefix", - ))) - } - }; - (name, Some(format!("{}", dir.display()))) + let dir = path.parent().map(|x| format!("{}", x.display())); + (name, dir) }; /* Fill in metadata */ @@ -293,10 +341,10 @@ impl Header { owner: Option, ) -> Result { let mut header = Header::default(); - let (filename, prefix) = if filename.len() < 100 { - (filename.to_string(), None) - } else { - // Deal with file names longer than 100 bytes + let (filename, prefix) = { + // Original tar has a maximum file name length of 100 bytes. The ustar + // revision allows storing the path prefix separately, with 100 bytes + // reserved for the file name and 150 bytes for the rest of the path. let path = PathBuf::from(&filename); let name = match path.file_name().and_then(|n| n.to_str()) { Some(n) => n.to_string(), @@ -307,16 +355,8 @@ impl Header { ))) } }; - let dir = match path.parent() { - Some(d) => d, - None => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "Cannot get path prefix", - ))) - } - }; - (name, Some(format!("{}", dir.display()))) + let dir = path.parent().map(|x| format!("{}", x.display())); + (name, dir) }; header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); let mode = format!("{:07o}", meta.st_mode()); @@ -431,7 +471,7 @@ fn get_username_for_uid<'a>(uid: u32) -> Result<&'a str, std::str::Utf8Error> { user.to_str() } -pub fn get_groupname_for_gid<'a>(gid: u32) -> Result<&'a str, std::str::Utf8Error> { +fn get_groupname_for_gid<'a>(gid: u32) -> Result<&'a str, std::str::Utf8Error> { let group = unsafe { let gr = libc::getgrgid(gid); let name = (*gr).gr_name; diff --git a/src/version/rapid.rs b/src/version/rapid.rs index 0be2946..e7842fa 100644 --- a/src/version/rapid.rs +++ b/src/version/rapid.rs @@ -165,4 +165,30 @@ mod test { }) ); } + + #[test] + fn rapid_num_eq() { + let rapid = Rapid { major: 42, minor: 0 }; + assert_eq!(rapid, 42); + } + + #[test] + fn rapid_num_gt() { + let rapid = Rapid { major: 1, minor: 42 }; + assert!(rapid > 1); + } + + #[test] + fn rapid_semver_eq() { + let rapid = Rapid { major: 42, minor: 69 }; + let semver = SemVer { major: 42, minor: 69, patch: 0 }; + assert_eq!(rapid, semver); + } + + #[test] + fn rapid_semver_lt() { + let rapid = Rapid { major: 42, minor: 69 }; + let semver = SemVer { major: 42, minor: 69, patch: 1 }; + assert!(rapid < semver); + } } diff --git a/test/2.tar b/test/2.tar index 3e7ac15362f5829b5622aa8d1698d0ed7cab964b..98902ce9c96775fa2ed4c0b8194684c56b3bd9ca 100644 GIT binary patch delta 44 ycmZn&Xb506)GMhdnYb{1Vu}&Ffw`G6gMz_iLB^EHjEoU1C8@