Extend tar functionality to turn nodes and archives into byte vectors;
Implement package creation using those methods;
This commit is contained in:
parent
318968db4b
commit
ffcd6820d0
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -398,6 +398,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"cli",
|
||||
"deku",
|
||||
"rayon",
|
||||
"ron",
|
||||
"serde",
|
||||
|
@ -15,9 +15,11 @@ path = "src/hpk.rs"
|
||||
|
||||
[workspace.dependencies]
|
||||
clap = "4.1"
|
||||
deku = "0.16"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
deku.workspace = true
|
||||
cli = { path = "cli" }
|
||||
rayon = "1.7"
|
||||
ron = "0.8"
|
||||
|
@ -20,7 +20,7 @@ impl Hooks {
|
||||
match self {
|
||||
Self::Man => makeinfo(),
|
||||
Self::GlibSchema => compile_schemas(),
|
||||
Self::Info(path) => install_info(&path),
|
||||
Self::Info(path) => install_info(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use crate::Entry;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
error::Error,
|
||||
ffi::OsStr,
|
||||
fmt::Write,
|
||||
fs,
|
||||
io::Read,
|
||||
@ -13,13 +14,14 @@ use tar::{Node, Owner};
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Item {
|
||||
pub entry: Entry,
|
||||
pub node: Node,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn try_create(path: &Path) -> Result<Self, Box<dyn Error>> {
|
||||
let meta = fs::metadata(path)?;
|
||||
let filename = format!("/{}", path.display());
|
||||
let path = fix_path(path);
|
||||
let meta = fs::metadata(&path)?;
|
||||
let filename = format!("{}", path.display());
|
||||
let owner = Some(Owner::default());
|
||||
if meta.is_file() {
|
||||
let mut data = vec![];
|
||||
@ -41,24 +43,44 @@ impl Item {
|
||||
mode,
|
||||
size,
|
||||
},
|
||||
node: Node::read_data_to_tar(&data, &filename, &meta, owner)?,
|
||||
data: Node::read_data_to_tar(&data, &filename, &meta, owner)?.to_vec()?,
|
||||
})
|
||||
} 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)?,
|
||||
data: Node::read_data_to_tar(&[0; 0], &filename, &meta, owner)?.to_vec()?,
|
||||
})
|
||||
} 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)?,
|
||||
data: Node::read_data_to_tar(&[0; 0], &filename, &meta, owner)?.to_vec()?,
|
||||
})
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_path(path: &Path) -> PathBuf {
|
||||
if path.is_file() || path.is_symlink() {
|
||||
match path.ancestors().last().and_then(Path::to_str) {
|
||||
Some("etc" | "var") => {
|
||||
let ext = if let Some(x) = path.extension().and_then(OsStr::to_str) {
|
||||
format!("{x}.new")
|
||||
} else {
|
||||
"new".to_string()
|
||||
};
|
||||
let mut path = path.to_path_buf();
|
||||
path.set_extension(ext);
|
||||
path
|
||||
}
|
||||
_ => path.to_path_buf(),
|
||||
}
|
||||
} else {
|
||||
path.to_path_buf()
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ mod version;
|
||||
pub use {
|
||||
hooks::Hooks,
|
||||
item::Item,
|
||||
package::{Dependency, Package, Specs},
|
||||
package::{create as create_package, Dependency, Package, Specs},
|
||||
plist::*,
|
||||
repository::Repository,
|
||||
tar,
|
||||
|
@ -1,9 +1,25 @@
|
||||
use crate::Item;
|
||||
use std::path::PathBuf;
|
||||
use zstd::Encoder;
|
||||
|
||||
mod dependency;
|
||||
mod specs;
|
||||
|
||||
use {
|
||||
crate::{Plist, Version},
|
||||
crate::{Entry, Plist, Version},
|
||||
rayon::prelude::*,
|
||||
ron::ser::{to_string_pretty, PrettyConfig},
|
||||
serde::{Deserialize, Serialize},
|
||||
std::{
|
||||
error::Error,
|
||||
fs::File,
|
||||
io::{BufWriter, Write},
|
||||
path::Path,
|
||||
},
|
||||
tar::Node,
|
||||
walkdir::WalkDir,
|
||||
};
|
||||
|
||||
pub use {dependency::Dependency, specs::Specs};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
@ -18,7 +34,7 @@ pub struct Group {
|
||||
pub gid: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Package {
|
||||
/// The name of the package minus all version information
|
||||
pub name: String,
|
||||
@ -48,3 +64,83 @@ pub struct Package {
|
||||
/// an optional post installation shell script to be run
|
||||
pub post_install: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Specs> for Package {
|
||||
fn from(value: Specs) -> Self {
|
||||
let mut package = Package::default();
|
||||
package.name = value.name;
|
||||
package.version = value.version;
|
||||
package.release = value.release;
|
||||
package.description = value.description;
|
||||
package.long_description = value.long_description;
|
||||
package.appstream_data = value.appstream_data;
|
||||
package.dependencies = value.dependencies;
|
||||
package.users = value.users;
|
||||
package.groups = value.groups;
|
||||
package.post_install = value.post_install;
|
||||
package
|
||||
}
|
||||
}
|
||||
|
||||
impl Package {
|
||||
fn as_ron(&self) -> Result<String, ron::Error> {
|
||||
let cfg = PrettyConfig::new().struct_names(true);
|
||||
to_string_pretty(self, cfg)
|
||||
}
|
||||
|
||||
pub fn save_ron_and_create_tar_node(&self) -> Result<Node, Box<dyn Error>> {
|
||||
let buf = File::open("package.ron")?;
|
||||
let meta = buf.metadata()?;
|
||||
let s = self.as_ron()?;
|
||||
let mut writer = BufWriter::new(buf);
|
||||
writer.write_all(s.as_bytes())?;
|
||||
let node = Node::read_data_to_tar(s.as_bytes(), "package.ron", &meta, None)?;
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
pub fn fullname(&self) -> String {
|
||||
format!("{}-{}_{}", self.name, self.version, self.release)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(path: &Path, specs: Specs) -> Result<(), Box<dyn Error>> {
|
||||
let mut items = WalkDir::new(path)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>()
|
||||
.par_iter()
|
||||
.filter(|x| x.is_ok())
|
||||
.filter_map(|x| {
|
||||
Item::try_create(x.as_ref().unwrap().path().to_path_buf().as_path()).ok()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut plist = Plist::default();
|
||||
let mut archive = vec![];
|
||||
let mut totalsize = 0;
|
||||
while let Some(item) = items.pop() {
|
||||
match &item.entry {
|
||||
Entry::File {
|
||||
path: _,
|
||||
sha256sum: _,
|
||||
mode: _,
|
||||
size,
|
||||
} => totalsize += size,
|
||||
_ => {}
|
||||
}
|
||||
plist.entries.push(item.entry);
|
||||
archive.extend(item.data);
|
||||
}
|
||||
let mut package: Package = specs.into();
|
||||
package.plist = plist;
|
||||
package.size = totalsize;
|
||||
let node = package.save_ron_and_create_tar_node()?.to_vec()?;
|
||||
archive.extend(node);
|
||||
archive.write_all(&[0; 9216])?;
|
||||
let name = package.fullname();
|
||||
let mut path = PathBuf::from(&name);
|
||||
path.set_extension("tar.zst");
|
||||
let fd = File::open(&path)?;
|
||||
let mut writer = Encoder::new(fd, 0)?;
|
||||
writer.write_all(&archive)?;
|
||||
let _fd = writer.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use {
|
||||
crate::{Dependency, Version},
|
||||
clap::ArgMatches,
|
||||
serde::{Deserialize, Serialize},
|
||||
std::{env, error::Error},
|
||||
std::{env, error::Error, ffi::OsStr},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
@ -35,35 +35,30 @@ pub struct Specs {
|
||||
impl TryFrom<&ArgMatches> for Specs {
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
|
||||
let mut specs = Specs::default();
|
||||
let mut name: Option<String> = None;
|
||||
let mut version: Option<Version> = None;
|
||||
let cwd = env::current_dir()?;
|
||||
if let Some(dir) = cwd.file_name() {
|
||||
if let Some(n) = dir.to_str() {
|
||||
if let Some((n, v)) = n.split_once('-') {
|
||||
if let Some(dir) = cwd.file_name().and_then(OsStr::to_str) {
|
||||
if let Some((n, v)) = dir.split_once('-') {
|
||||
name = Some(n.to_string());
|
||||
if let Ok(v) = v.parse() {
|
||||
version = Some(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(name) = matches.get_one::<String>("name") {
|
||||
specs.name = name.to_string();
|
||||
} else {
|
||||
if let Some(n) = name {
|
||||
} else if let Some(n) = name {
|
||||
specs.name = n;
|
||||
}
|
||||
}
|
||||
if let Some(version) = matches.get_one::<String>("package-version") {
|
||||
specs.version = version.parse()?;
|
||||
} else {
|
||||
if let Some(v) = version {
|
||||
} else if let Some(v) = version {
|
||||
specs.version = v;
|
||||
}
|
||||
}
|
||||
if let Some(release) = matches.get_one::<usize>("release") {
|
||||
specs.release = *release as u8;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use {
|
||||
walkdir::WalkDir,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Plist {
|
||||
pub entries: Vec<Entry>,
|
||||
}
|
||||
|
@ -33,6 +33,33 @@ impl Default for Version {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Number(n) => write!(f, "{n}"),
|
||||
Self::SemVer {
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
} => {
|
||||
let v = SemVer {
|
||||
major: *major,
|
||||
minor: *minor,
|
||||
patch: *patch,
|
||||
};
|
||||
write!(f, "{v}")
|
||||
}
|
||||
Self::Git { hash, datetime } => {
|
||||
let v = GitRev {
|
||||
hash: hash.to_owned(),
|
||||
datetime: *datetime,
|
||||
};
|
||||
write!(f, "{v}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SemVer> for Version {
|
||||
fn from(value: SemVer) -> Self {
|
||||
Self::SemVer {
|
||||
@ -45,7 +72,10 @@ impl From<SemVer> for Version {
|
||||
|
||||
impl From<GitRev> for Version {
|
||||
fn from(value: GitRev) -> Self {
|
||||
Self::Git { hash: value.hash, datetime: value.datetime }
|
||||
Self::Git {
|
||||
hash: value.hash,
|
||||
datetime: value.datetime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +86,7 @@ impl From<u32> for Version {
|
||||
}
|
||||
|
||||
impl PartialOrd for Version {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match (self, other) {
|
||||
(Self::Number(s), Self::Number(o)) => s.partial_cmp(o),
|
||||
|
@ -70,7 +70,7 @@ impl FromStr for SemVer {
|
||||
let split = s.split('.').collect::<Vec<_>>();
|
||||
match split.len() {
|
||||
3 => {
|
||||
let major = split.get(0).unwrap().parse::<u8>()?;
|
||||
let major = split.first().unwrap().parse::<u8>()?;
|
||||
let minor = split.get(1).unwrap().parse::<u8>()?;
|
||||
let patch = split.get(2).unwrap().parse::<u8>()?;
|
||||
Ok(Self {
|
||||
|
@ -5,6 +5,6 @@ edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
[dependencies]
|
||||
deku = "0.16"
|
||||
deku = { workspace = true }
|
||||
libc = "0.2"
|
||||
thiserror = "1.0.40"
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufReader},
|
||||
io::{self, BufReader, Write},
|
||||
};
|
||||
|
||||
mod error;
|
||||
@ -12,11 +12,21 @@ pub use {
|
||||
node::Node,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Archive {
|
||||
pub nodes: Vec<Node>,
|
||||
}
|
||||
|
||||
impl Archive {
|
||||
pub fn to_vec(self) -> Result<Vec<u8>, Error> {
|
||||
let mut buf = vec![];
|
||||
for node in self.nodes {
|
||||
buf.extend(node.to_vec()?);
|
||||
}
|
||||
buf.write_all(&[0; 9216])?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Write out a vector of `TarNodes` to a file or something that implements ``std::io::Write`` and ``std::io::Copy``.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -13,6 +13,14 @@ pub struct Node {
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn to_vec(self) -> Result<Vec<u8>, DekuError> {
|
||||
let mut buf = self.header.to_bytes()?;
|
||||
for block in self.data {
|
||||
buf.extend(block.to_vec());
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Write out a single file within the tar to a file or something with a ``std::io::Write`` trait.
|
||||
pub fn write<T: io::Write>(self, mut input: T) -> Result<usize, Error> {
|
||||
input.write_all(&self.header.to_bytes()?)?;
|
||||
@ -26,7 +34,7 @@ impl Node {
|
||||
}
|
||||
|
||||
/// Read a TarNode in from a file or something with a ``std::io::Read`` trait.
|
||||
pub fn read<T: io::Read>(mut input: T) -> Result<Node, Error> {
|
||||
pub fn read<T: io::Read>(mut input: T) -> Result<Self, Error> {
|
||||
let mut h = vec![0u8; 512];
|
||||
input.read_exact(&mut h)?;
|
||||
|
||||
@ -49,7 +57,7 @@ impl Node {
|
||||
}
|
||||
|
||||
/// Open and read a file from the ``filename`` argument to a TarNode.
|
||||
pub fn read_file_to_tar(filename: String) -> Result<Node, Error> {
|
||||
pub fn read_file_to_tar(filename: String) -> Result<Self, Error> {
|
||||
let header = Header::new(&filename)?;
|
||||
if header.link_indicator[0] != FileType::Normal as u8 {
|
||||
return Ok(Node {
|
||||
@ -72,7 +80,7 @@ impl Node {
|
||||
filename: &str,
|
||||
meta: &Metadata,
|
||||
owner: Option<Owner>,
|
||||
) -> Result<Node, Error> {
|
||||
) -> Result<Self, Error> {
|
||||
let header = Header::new_from_meta(filename, meta, owner)?;
|
||||
if header.link_indicator[0] != FileType::Normal as u8 {
|
||||
return Ok(Node {
|
||||
|
Loading…
Reference in New Issue
Block a user