Add Item struct, which contains both a tar::Node and a plist entry.

This struct can be created from just a `Path` parameter, allowing to
read the data contained in a file only once during package creation or
extraction.
This commit is contained in:
Nathan Fisher 2023-03-23 14:43:25 -04:00
parent 4f44290a48
commit 85a85a3810
11 changed files with 91 additions and 13 deletions

53
src/item/mod.rs Normal file
View File

@ -0,0 +1,53 @@
use crate::Entry;
use sha2::{Digest, Sha256};
use std::{error::Error, fmt::Write, fs, path::{Path, PathBuf}, io::Read, os::unix::fs::MetadataExt};
use tar::{Node, Owner};
#[derive(Clone, Debug)]
pub struct Item {
pub entry: Entry,
pub node: Node,
}
impl Item {
pub fn try_create(path: &Path) -> Result<Self, Box<dyn Error>> {
let meta = fs::metadata(path)?;
let filename = format!("/{}", path.display());
let owner = Some(Owner::default());
if meta.is_file() {
let mut data = vec![];
let mut fd = fs::File::open(path)?;
let path = PathBuf::from(&filename);
let size = fd.read_to_end(&mut data)?;
let mut sha256sum = String::new();
let mut hasher = Sha256::new();
hasher.update(&data);
let res = hasher.finalize();
for c in res {
write!(sha256sum, "{c:02x}")?;
}
let node = Node::read_data_to_tar(&data, &filename, &meta, owner)?;
let mode = meta.mode();
Ok(Self {
entry: Entry::File { path, sha256sum, mode, size },
node,
})
} else if meta.is_dir() {
let mode = meta.mode();
let path = PathBuf::from(&filename);
Ok(Self {
entry: Entry::Directory { path, mode },
node: Node::read_data_to_tar(&[0; 0], &filename, &meta, owner)?,
})
} else if meta.is_symlink() {
let target = fs::read_link(path)?;
let path = PathBuf::from(&filename);
Ok(Self {
entry: Entry::Link { path, target },
node: Node::read_data_to_tar(&[0; 0], &filename, &meta, owner)?,
})
} else {
unreachable!();
}
}
}

View File

@ -1,8 +1,10 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
mod item;
mod package; mod package;
mod plist; mod plist;
mod version; mod version;
pub use { pub use {
item::Item,
package::{Dependency, Package}, package::{Dependency, Package},
plist::*, plist::*,
version::*, version::*,

View File

@ -4,7 +4,7 @@ use {
serde::{Deserialize, Serialize}, serde::{Deserialize, Serialize},
}; };
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Dependency { pub struct Dependency {
name: String, name: String,
version: (Option<Version>, Option<Version>), version: (Option<Version>, Option<Version>),

View File

@ -5,7 +5,19 @@ use {
serde::{Deserialize, Serialize}, serde::{Deserialize, Serialize},
}; };
#[derive(Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct User {
pub name: String,
pub uid: Option<u32>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Group {
pub name: String,
pub gid: Option<u32>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Package { pub struct Package {
name: String, name: String,
version: Version, version: Version,
@ -13,4 +25,6 @@ pub struct Package {
plist: Plist, plist: Plist,
size: usize, size: usize,
dependencies: Vec<Dependency>, dependencies: Vec<Dependency>,
users: Option<Vec<User>>,
groups: Option<Vec<Group>>,
} }

View File

@ -13,7 +13,7 @@ use {
walkdir::WalkDir, walkdir::WalkDir,
}; };
#[derive(Deserialize, Serialize)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Plist { pub struct Plist {
pub entries: Vec<Entry>, pub entries: Vec<Entry>,
} }
@ -35,7 +35,7 @@ impl TryFrom<&Path> for Plist {
} }
} }
#[derive(Deserialize, Serialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum Entry { pub enum Entry {
File { File {
path: PathBuf, path: PathBuf,
@ -89,3 +89,4 @@ impl TryFrom<&Path> for Entry {
} }
} }
} }

View File

@ -2,7 +2,7 @@ use std::cmp::Ordering;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct GitRev { pub struct GitRev {
hash: String, hash: String,
datetime: u64, datetime: u64,

View File

@ -5,7 +5,7 @@ mod semver;
pub use {gitrev::GitRev, semver::SemVer}; pub use {gitrev::GitRev, semver::SemVer};
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum Version { pub enum Version {
Number(u32), Number(u32),
SemVer(SemVer), SemVer(SemVer),

View File

@ -3,7 +3,7 @@ use {
std::cmp::Ordering, std::cmp::Ordering,
}; };
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct SemVer { pub struct SemVer {
pub major: u32, pub major: u32,
pub minor: u32, pub minor: u32,

View File

@ -244,7 +244,11 @@ impl Header {
Ok(header) Ok(header)
} }
pub fn new_from_meta(filename: &str, meta: &Metadata, owner: Option<Owner>) -> Result<Self, Error> { pub fn new_from_meta(
filename: &str,
meta: &Metadata,
owner: Option<Owner>,
) -> Result<Self, Error> {
let mut header = Header::default(); let mut header = Header::default();
header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
let mode = format!("{:07o}", meta.st_mode()); let mode = format!("{:07o}", meta.st_mode());
@ -256,7 +260,7 @@ impl Header {
gid: meta.st_gid(), gid: meta.st_gid(),
username: get_username_for_uid(meta.st_uid())?.into(), username: get_username_for_uid(meta.st_uid())?.into(),
groupname: get_groupname_for_gid(meta.st_gid())?.into(), groupname: get_groupname_for_gid(meta.st_gid())?.into(),
} },
}; };
let uid = format!("{:07o}", owner.uid); let uid = format!("{:07o}", owner.uid);
header.uid[..uid.len()].copy_from_slice(uid.as_bytes()); header.uid[..uid.len()].copy_from_slice(uid.as_bytes());
@ -365,4 +369,3 @@ pub fn get_groupname_for_gid<'a>(gid: u32) -> Result<&'a str, std::str::Utf8Erro
}; };
group.to_str() group.to_str()
} }

View File

@ -8,7 +8,7 @@ mod header;
mod node; mod node;
pub use { pub use {
error::Error, error::Error,
header::{FileType, Header}, header::{FileType, Header, Owner},
node::Node, node::Node,
}; };

View File

@ -1,4 +1,4 @@
use crate::{Error, FileType, Header, header::Owner}; use crate::{header::Owner, Error, FileType, Header};
use deku::prelude::*; use deku::prelude::*;
use std::{ use std::{
fs::{File, Metadata}, fs::{File, Metadata},
@ -67,7 +67,12 @@ impl Node {
} }
/// Create a Node from in memory data, given the filename and metadata /// Create a Node from in memory data, given the filename and metadata
pub fn read_data_to_tar(data: &[u8], filename: &str, meta: &Metadata, owner: Option<Owner>) -> Result<Node, Error> { pub fn read_data_to_tar(
data: &[u8],
filename: &str,
meta: &Metadata,
owner: Option<Owner>,
) -> Result<Node, Error> {
let header = Header::new_from_meta(filename, meta, owner)?; let header = Header::new_from_meta(filename, meta, owner)?;
if header.link_indicator[0] != FileType::Normal as u8 { if header.link_indicator[0] != FileType::Normal as u8 {
return Ok(Node { return Ok(Node {