diff --git a/src/lib.rs b/src/lib.rs index 084113d..fb56ef0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,12 @@ -pub mod tar; mod package; mod plist; +pub mod tar; mod version; pub use { deku, - package::{Dependency, Package, Specs}, + package::{Arch, Dependency, Group, Package, Specs, User}, plist::*, - ron, - sha2, + ron, sha2, version::*, }; diff --git a/src/package/arch.rs b/src/package/arch.rs index 5f10d1f..59f1c01 100644 --- a/src/package/arch.rs +++ b/src/package/arch.rs @@ -1,10 +1,10 @@ use serde::{Deserialize, Serialize}; -use std::{fmt, str::FromStr, error::Error}; +use std::{error::Error, fmt, str::FromStr}; #[cfg(target_arch = "arm")] pub const HOST_ARCH: Arch = Arch::armv7l; #[cfg(target_arch = "aarch64")] -pub const HOST_ARCH: Arch = Arch::aarch64; +pub const HOST_ARCH: Arch = Arch::aarch64; #[cfg(target_arch = "x86")] pub const HOST_ARCH: Arch = Arch::i486; #[cfg(target_arch = "riscv64")] @@ -33,16 +33,20 @@ impl Default for Arch { impl fmt::Display for Arch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", match self { - Self::armv7l => "armv7l", - Self::aarch64 => "aarch64", - Self::i486 => "i486", - Self::i586 => "i586", - Self::i686 => "i686", - Self::riscv64 => "riscv64", - Self::x86_64 => "x86_64", - Self::any => "any", - }) + write!( + f, + "{}", + match self { + Self::armv7l => "armv7l", + Self::aarch64 => "aarch64", + Self::i486 => "i486", + Self::i586 => "i586", + Self::i686 => "i686", + Self::riscv64 => "riscv64", + Self::x86_64 => "x86_64", + Self::any => "any", + } + ) } } @@ -69,7 +73,7 @@ impl FromStr for Arch { "arm64" | "aarch64" | "armv8" => Ok(Self::aarch64), "riscv" | "riscv64" => Ok(Self::riscv64), "any" => Ok(Self::any), - _ => Err(ParseArchError) + _ => Err(ParseArchError), } } } diff --git a/src/package/mod.rs b/src/package/mod.rs index ffe2a7d..0020cd9 100644 --- a/src/package/mod.rs +++ b/src/package/mod.rs @@ -3,6 +3,7 @@ mod dependency; mod specs; use { + crate::tar::{Node, Owner}, crate::{Plist, Version}, ron::ser::{to_string_pretty, PrettyConfig}, serde::{Deserialize, Serialize}, @@ -13,7 +14,6 @@ use { io::{BufWriter, Write}, path::Path, }, - crate::tar::{Node, Owner}, }; pub use {arch::Arch, dependency::Dependency, specs::Specs}; @@ -88,10 +88,7 @@ impl Package { to_string_pretty(self, cfg) } - pub fn save_ron_and_create_tar_node( - &self, - outdir: &Path, - ) -> Result> { + pub fn save_ron_and_create_tar_node(&self, outdir: &Path) -> Result> { if !outdir.exists() { fs::create_dir_all(outdir)?; } @@ -113,12 +110,18 @@ impl Package { /// Returns the formatted full package name including version and release strings pub fn fullname(&self) -> String { - format!("{}-{}_{}_{}", self.name, self.version, self.release, self.arch) + format!( + "{}-{}_{}_{}", + self.name, self.version, self.release, self.arch + ) } /// Returns the name of the package archive pub fn archive_name(&self) -> String { - format!("{}-{}_{}_{}.tar.zstd", self.name, self.version, self.release, self.arch) + format!( + "{}-{}_{}_{}.tar.zstd", + self.name, self.version, self.release, self.arch + ) } /// Tests whether this package is an update for another diff --git a/src/tar/header.rs b/src/tar/header.rs index bfc363a..697f974 100644 --- a/src/tar/header.rs +++ b/src/tar/header.rs @@ -125,27 +125,38 @@ impl Header { /// use hpk_package::tar::Header; /// /// let header = Header::new("test/1.txt").unwrap(); - /// assert_eq!(header.filename().unwrap(), "test/1.txt"); + /// 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 != 0 { - write!(s, "{}", char::from(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)?; @@ -155,10 +166,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)?; @@ -168,10 +179,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)?; @@ -181,10 +192,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) @@ -193,10 +204,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) @@ -215,31 +226,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) + } } - pub fn path(&self) -> Result { - let mut p = PathBuf::from(&self.prefix()?); - p.push(&self.filename()?); - Ok(p) + /// 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(), @@ -250,16 +291,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 */ @@ -309,10 +342,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(), @@ -323,16 +356,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()); @@ -447,7 +472,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/tar/mod.rs b/src/tar/mod.rs index a84f80a..004b94e 100644 --- a/src/tar/mod.rs +++ b/src/tar/mod.rs @@ -1,6 +1,7 @@ use std::{ fs::File, io::{self, BufReader, Write}, + path::PathBuf, }; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; @@ -156,18 +157,25 @@ impl Archive { /// /// ``` /// use hpk_package::tar::Archive; - /// let archive = Archive::new("test/1.tar").unwrap(); - /// let node = archive.get("1.txt"); + /// let archive = Archive::new("test/1.txt").unwrap(); + /// let node = archive.get("test/1.txt"); /// assert!(node.is_some()); /// ``` pub fn get(&self, filename: &str) -> Option { self.nodes.par_iter().find_any(|x| { - if let Ok(name) = x.header.filename() { - if &name == filename { - return true; - } - } - false + x.header.file_path() == Ok(PathBuf::from(filename)) }).cloned() } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn open_get() { + let archive = Archive::open("test/1.tar").unwrap(); + let node = archive.get("1.txt"); + assert!(node.is_some()); + } +} 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 bc84f02..7593697 100644 Binary files a/test/2.tar and b/test/2.tar differ