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)]
mod item;
mod package;
mod plist;
mod version;
pub use {
item::Item,
package::{Dependency, Package},
plist::*,
version::*,

View File

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

View File

@ -5,7 +5,19 @@ use {
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 {
name: String,
version: Version,
@ -13,4 +25,6 @@ pub struct Package {
plist: Plist,
size: usize,
dependencies: Vec<Dependency>,
users: Option<Vec<User>>,
groups: Option<Vec<Group>>,
}

View File

@ -13,7 +13,7 @@ use {
walkdir::WalkDir,
};
#[derive(Deserialize, Serialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Plist {
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 {
File {
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};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct GitRev {
hash: String,
datetime: u64,

View File

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

View File

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

View File

@ -244,7 +244,11 @@ impl 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();
header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
let mode = format!("{:07o}", meta.st_mode());
@ -256,7 +260,7 @@ impl Header {
gid: meta.st_gid(),
username: get_username_for_uid(meta.st_uid())?.into(),
groupname: get_groupname_for_gid(meta.st_gid())?.into(),
}
},
};
let uid = format!("{:07o}", owner.uid);
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()
}

View File

@ -8,7 +8,7 @@ mod header;
mod node;
pub use {
error::Error,
header::{FileType, Header},
header::{FileType, Header, Owner},
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 std::{
fs::{File, Metadata},
@ -67,7 +67,12 @@ 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, 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)?;
if header.link_indicator[0] != FileType::Normal as u8 {
return Ok(Node {