Split out hpk-package into separate crate; Implement Repository::build()
This commit is contained in:
parent
9126e1da93
commit
082c215b44
105
Cargo.lock
generated
105
Cargo.lock
generated
@ -127,9 +127,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
@ -226,7 +226,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"scratch",
|
"scratch",
|
||||||
"syn 2.0.12",
|
"syn 2.0.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -243,7 +243,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.12",
|
"syn 2.0.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -380,24 +380,36 @@ dependencies = [
|
|||||||
name = "hpk"
|
name = "hpk"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"hpk-package",
|
||||||
"deku",
|
|
||||||
"rayon",
|
"rayon",
|
||||||
"ron",
|
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
|
||||||
"tar",
|
|
||||||
"ureq",
|
"ureq",
|
||||||
"url",
|
"url",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"zstd",
|
"zstd",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hpk-package"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://git.hitchhiker-linux.org/jeang3nie/hpk-package.git#90b163eb1b7373085d3c5eb93919eb8ecfedb219"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"deku",
|
||||||
|
"libc",
|
||||||
|
"rayon",
|
||||||
|
"ron",
|
||||||
|
"serde",
|
||||||
|
"sha2",
|
||||||
|
"thiserror",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.55"
|
version = "0.1.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "716f12fbcfac6ffab0a5e9ec51d0a0ff70503742bb2dc7b99396394c9dc323f0"
|
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_system_properties",
|
"android_system_properties",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
@ -574,9 +586,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.54"
|
version = "1.0.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
|
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -757,7 +769,7 @@ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.12",
|
"syn 2.0.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -807,9 +819,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.12"
|
version = "2.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
|
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -822,15 +834,6 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tar"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"deku",
|
|
||||||
"libc",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -857,7 +860,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.12",
|
"syn 2.0.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1110,9 +1113,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2649ff315bee4c98757f15dac226efe3d81927adbb6e882084bb1ee3e0c330a7"
|
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
@ -1134,17 +1137,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f8996d3f43b4b2d44327cd71b7b0efd1284ab60e6e9d0e8b630e18555d87d3e"
|
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm 0.47.0",
|
"windows_aarch64_gnullvm 0.48.0",
|
||||||
"windows_aarch64_msvc 0.47.0",
|
"windows_aarch64_msvc 0.48.0",
|
||||||
"windows_i686_gnu 0.47.0",
|
"windows_i686_gnu 0.48.0",
|
||||||
"windows_i686_msvc 0.47.0",
|
"windows_i686_msvc 0.48.0",
|
||||||
"windows_x86_64_gnu 0.47.0",
|
"windows_x86_64_gnu 0.48.0",
|
||||||
"windows_x86_64_gnullvm 0.47.0",
|
"windows_x86_64_gnullvm 0.48.0",
|
||||||
"windows_x86_64_msvc 0.47.0",
|
"windows_x86_64_msvc 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1155,9 +1158,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "831d567d53d4f3cb1db332b68e6e2b6260228eb4d99a777d8b2e8ed794027c90"
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
@ -1167,9 +1170,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a42d54a417c60ce4f0e31661eed628f0fa5aca73448c093ec4d45fab4c51cdf"
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
@ -1179,9 +1182,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1925beafdbb22201a53a483db861a5644123157c1c3cee83323a2ed565d71e3"
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
@ -1191,9 +1194,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a8ef8f2f1711b223947d9b69b596cf5a4e452c930fb58b6fc3fdae7d0ec6b31"
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
@ -1203,9 +1206,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7acaa0c2cf0d2ef99b61c308a0c3dbae430a51b7345dedec470bd8f53f5a3642"
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
@ -1215,9 +1218,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5a0628f71be1d11e17ca4a0e9e15b3a5180f6fbf1c2d55e3ba3f850378052c1"
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
@ -1227,9 +1230,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.47.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d6e62c256dc6d40b8c8707df17df8d774e60e39db723675241e7c15e910bce7"
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
|
15
Cargo.toml
15
Cargo.toml
@ -6,25 +6,12 @@ license = "GPL-3.0-only"
|
|||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[workspace]
|
|
||||||
members = [ "tar" ]
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
deku = "0.16"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deku.workspace = true
|
hpk-package = { git = "https://git.hitchhiker-linux.org/jeang3nie/hpk-package.git" }
|
||||||
rayon = "1.7"
|
rayon = "1.7"
|
||||||
ron = "0.8"
|
|
||||||
sha2 = "0.10"
|
|
||||||
tar = { path = "tar" }
|
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
zstd = "0.12"
|
zstd = "0.12"
|
||||||
|
|
||||||
[dependencies.chrono]
|
|
||||||
version = "0.4"
|
|
||||||
features = ["serde"]
|
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use {
|
use {
|
||||||
crate::{Entry, Item, Package, Plist, Specs},
|
crate::{Item, Package, Plist, Specs},
|
||||||
|
hpk_package::Entry,
|
||||||
rayon::prelude::{IntoParallelRefIterator, ParallelIterator},
|
rayon::prelude::{IntoParallelRefIterator, ParallelIterator},
|
||||||
std::{
|
std::{
|
||||||
borrow::BorrowMut,
|
borrow::BorrowMut,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use {
|
use {
|
||||||
crate::{Package, Repository, Version},
|
crate::{Package, Repository, Version},
|
||||||
|
hpk_package::ron::{self, ser::PrettyConfig},
|
||||||
rayon::prelude::*,
|
rayon::prelude::*,
|
||||||
ron::ser::PrettyConfig,
|
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
std::{
|
std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::Entry;
|
use hpk_package::{
|
||||||
use sha2::{Digest, Sha256};
|
sha2::{Digest, Sha256},
|
||||||
|
tar::{Node, Owner},
|
||||||
|
Entry,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
@ -9,7 +12,6 @@ use std::{
|
|||||||
os::unix::fs::MetadataExt,
|
os::unix::fs::MetadataExt,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use tar::{Node, Owner};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
|
@ -3,10 +3,7 @@ mod creator;
|
|||||||
mod db;
|
mod db;
|
||||||
mod hooks;
|
mod hooks;
|
||||||
mod item;
|
mod item;
|
||||||
mod package;
|
|
||||||
mod plist;
|
|
||||||
mod repository;
|
mod repository;
|
||||||
mod version;
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -14,12 +11,9 @@ pub use {
|
|||||||
creator::{Creator, Message},
|
creator::{Creator, Message},
|
||||||
db::Database,
|
db::Database,
|
||||||
hooks::Hooks,
|
hooks::Hooks,
|
||||||
|
hpk_package::{tar, Dependency, GitRev, Package, Plist, Rapid, SemVer, Specs, Version},
|
||||||
item::Item,
|
item::Item,
|
||||||
package::{Dependency, Package, Specs},
|
|
||||||
plist::*,
|
|
||||||
repository::Repository,
|
repository::Repository,
|
||||||
tar,
|
|
||||||
version::*,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DB: [&str; 4] = ["var", "db", "hpk", "db.ron.zstd"];
|
const DB: [&str; 4] = ["var", "db", "hpk", "db.ron.zstd"];
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
use {
|
|
||||||
super::Package,
|
|
||||||
crate::Version,
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct Dependency {
|
|
||||||
pub name: String,
|
|
||||||
pub version: (Option<Version>, Option<Version>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dependency {
|
|
||||||
#[allow(clippy::must_use_candidate)]
|
|
||||||
pub fn satisfied(&self, package: &Package) -> bool {
|
|
||||||
if self.name.as_str() == package.name.as_str() {
|
|
||||||
match &self.version {
|
|
||||||
(Some(low), Some(high)) => &package.version >= low && &package.version < high,
|
|
||||||
(Some(low), None) => &package.version >= low,
|
|
||||||
(None, Some(high)) => &package.version < high,
|
|
||||||
// no version requirements
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
mod dependency;
|
|
||||||
mod specs;
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::{Plist, Version},
|
|
||||||
ron::ser::{to_string_pretty, PrettyConfig},
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
std::{
|
|
||||||
error::Error,
|
|
||||||
fs,
|
|
||||||
fs::File,
|
|
||||||
io::{BufWriter, Write},
|
|
||||||
path::Path,
|
|
||||||
},
|
|
||||||
tar::{Node, Owner},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use {dependency::Dependency, specs::Specs};
|
|
||||||
|
|
||||||
#[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, Default, Serialize, Deserialize, PartialEq)]
|
|
||||||
/// the metadata associated with a package
|
|
||||||
pub struct Package {
|
|
||||||
/// The name of the package minus all version information
|
|
||||||
pub name: String,
|
|
||||||
/// The `Version` of the package
|
|
||||||
pub version: Version,
|
|
||||||
/// The release number for this package
|
|
||||||
pub release: u8,
|
|
||||||
/// a single line description of the package
|
|
||||||
pub description: String,
|
|
||||||
/// a more verbose description of the package
|
|
||||||
pub long_description: String,
|
|
||||||
/// an optional link to an
|
|
||||||
/// [AppStream](https://www.freedesktop.org/wiki/Distributions/AppStream/)
|
|
||||||
/// metadata file
|
|
||||||
pub appstream_data: Option<String>,
|
|
||||||
/// a listing of all files, directories and symlinks which are a part of
|
|
||||||
/// this package
|
|
||||||
pub plist: Plist,
|
|
||||||
/// the total size of this package
|
|
||||||
pub size: usize,
|
|
||||||
/// all of this package's runtime dependencies
|
|
||||||
pub dependencies: Vec<Dependency>,
|
|
||||||
/// an optional list of users to be created upon installation
|
|
||||||
pub users: Option<Vec<User>>,
|
|
||||||
/// an optional list of groups to be created upon installation
|
|
||||||
pub groups: Option<Vec<Group>>,
|
|
||||||
/// an optional post installation shell script to be run
|
|
||||||
pub post_install: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Specs> for Package {
|
|
||||||
fn from(value: Specs) -> Self {
|
|
||||||
Package {
|
|
||||||
name: value.name,
|
|
||||||
version: value.version,
|
|
||||||
release: value.release,
|
|
||||||
description: value.description,
|
|
||||||
long_description: value.long_description,
|
|
||||||
appstream_data: value.appstream_data,
|
|
||||||
dependencies: value.dependencies,
|
|
||||||
users: value.users,
|
|
||||||
groups: value.groups,
|
|
||||||
post_install: value.post_install,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Package {
|
|
||||||
fn as_ron(&self) -> Result<String, ron::Error> {
|
|
||||||
let cfg = PrettyConfig::new().struct_names(true);
|
|
||||||
to_string_pretty(self, cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn save_ron_and_create_tar_node(
|
|
||||||
&self,
|
|
||||||
outdir: &Path,
|
|
||||||
) -> Result<Node, Box<dyn Error>> {
|
|
||||||
if !outdir.exists() {
|
|
||||||
fs::create_dir_all(outdir)?;
|
|
||||||
}
|
|
||||||
let mut outfile = outdir.to_path_buf();
|
|
||||||
outfile.push("package.ron");
|
|
||||||
let fd = File::create(&outfile)?;
|
|
||||||
let s = self.as_ron()?;
|
|
||||||
let mut writer = BufWriter::new(&fd);
|
|
||||||
writer.write_all(s.as_bytes())?;
|
|
||||||
writer.flush()?;
|
|
||||||
let node = Node::read_data_to_tar(
|
|
||||||
s.as_bytes(),
|
|
||||||
"package.ron",
|
|
||||||
&outfile.metadata()?,
|
|
||||||
Some(Owner::default()),
|
|
||||||
)?;
|
|
||||||
Ok(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the formatted full package name including version and release strings
|
|
||||||
pub fn fullname(&self) -> String {
|
|
||||||
format!("{}-{}_{}", self.name, self.version, self.release)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether this package is an update for another
|
|
||||||
pub fn is_upgrade(&self, other: &Self) -> bool {
|
|
||||||
self.name == other.name
|
|
||||||
&& (self.version > other.version
|
|
||||||
|| (self.version == other.version && self.release > other.release))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
use {
|
|
||||||
super::{Group, User},
|
|
||||||
crate::{Dependency, Version},
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct Specs {
|
|
||||||
/// The name of the package minus all version information
|
|
||||||
pub name: String,
|
|
||||||
/// The `Version` of the package
|
|
||||||
pub version: Version,
|
|
||||||
/// The release number for this package
|
|
||||||
pub release: u8,
|
|
||||||
/// a single line description of the package
|
|
||||||
pub description: String,
|
|
||||||
/// a more verbose description of the package
|
|
||||||
pub long_description: String,
|
|
||||||
/// an optional link to an
|
|
||||||
/// [AppStream](https://www.freedesktop.org/wiki/Distributions/AppStream/)
|
|
||||||
/// metadata file
|
|
||||||
pub appstream_data: Option<String>,
|
|
||||||
/// all of this package's runtime dependencies
|
|
||||||
pub dependencies: Vec<Dependency>,
|
|
||||||
/// an optional list of users to be created upon installation
|
|
||||||
pub users: Option<Vec<User>>,
|
|
||||||
/// an optional list of groups to be created upon installation
|
|
||||||
pub groups: Option<Vec<Group>>,
|
|
||||||
/// an optional post installation shell script to be run
|
|
||||||
pub post_install: Option<String>,
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
use {
|
|
||||||
rayon::prelude::*,
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
sha2::{digest::Digest, Sha256},
|
|
||||||
std::{
|
|
||||||
error::Error,
|
|
||||||
fmt::Write,
|
|
||||||
fs,
|
|
||||||
io::Read,
|
|
||||||
os::unix::fs::MetadataExt,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
},
|
|
||||||
walkdir::WalkDir,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct Plist {
|
|
||||||
pub entries: Vec<Entry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Path> for Plist {
|
|
||||||
type Error = Box<dyn Error>;
|
|
||||||
|
|
||||||
fn try_from(value: &Path) -> Result<Self, Self::Error> {
|
|
||||||
let entries = WalkDir::new(value)
|
|
||||||
.into_iter()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.par_iter()
|
|
||||||
.filter(|x| x.is_ok())
|
|
||||||
.filter_map(|x| {
|
|
||||||
Entry::try_from(x.as_ref().unwrap().path().to_path_buf().as_path()).ok()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(Self { entries })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum Entry {
|
|
||||||
File {
|
|
||||||
path: PathBuf,
|
|
||||||
sha256sum: String,
|
|
||||||
mode: u32,
|
|
||||||
size: usize,
|
|
||||||
},
|
|
||||||
Directory {
|
|
||||||
path: PathBuf,
|
|
||||||
mode: u32,
|
|
||||||
},
|
|
||||||
Link {
|
|
||||||
path: PathBuf,
|
|
||||||
target: PathBuf,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Path> for Entry {
|
|
||||||
type Error = Box<dyn Error>;
|
|
||||||
|
|
||||||
fn try_from(value: &Path) -> Result<Self, Self::Error> {
|
|
||||||
let mut path = PathBuf::from("/");
|
|
||||||
path.push(value);
|
|
||||||
let meta = fs::metadata(value)?;
|
|
||||||
if meta.is_file() {
|
|
||||||
let mut buf = vec![];
|
|
||||||
let mut fd = fs::File::open(value)?;
|
|
||||||
let size = fd.read_to_end(&mut buf)?;
|
|
||||||
let mut sha256sum = String::new();
|
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(&buf);
|
|
||||||
let res = hasher.finalize();
|
|
||||||
for c in res {
|
|
||||||
write!(sha256sum, "{c:02x}")?;
|
|
||||||
}
|
|
||||||
let mode = meta.mode();
|
|
||||||
Ok(Self::File {
|
|
||||||
path,
|
|
||||||
sha256sum,
|
|
||||||
mode,
|
|
||||||
size,
|
|
||||||
})
|
|
||||||
} else if meta.is_dir() {
|
|
||||||
let mode = meta.mode();
|
|
||||||
Ok(Self::Directory { path, mode })
|
|
||||||
} else if meta.is_symlink() {
|
|
||||||
let target = fs::read_link(value)?;
|
|
||||||
Ok(Self::Link { path, target })
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,15 @@
|
|||||||
use std::sync::PoisonError;
|
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::Package,
|
crate::Package,
|
||||||
|
hpk_package::ron,
|
||||||
rayon::prelude::*,
|
rayon::prelude::*,
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
std::{collections::HashMap, sync::Mutex},
|
std::{
|
||||||
|
collections::HashMap,
|
||||||
|
error::Error,
|
||||||
|
io::Read,
|
||||||
|
sync::{mpsc::Sender, Mutex, PoisonError},
|
||||||
|
time::Duration,
|
||||||
|
},
|
||||||
url::Url,
|
url::Url,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -15,7 +20,43 @@ pub struct Repository {
|
|||||||
pub packages: HashMap<String, Package>,
|
pub packages: HashMap<String, Package>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Message {
|
||||||
|
ContentLength(usize),
|
||||||
|
BytesRead(usize),
|
||||||
|
Failure(String),
|
||||||
|
Success(usize),
|
||||||
|
}
|
||||||
|
|
||||||
impl Repository {
|
impl Repository {
|
||||||
|
pub fn build(name: &str, url: &Url, sender: Sender<Message>) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let url = url.join("packages.ron.zstd")?;
|
||||||
|
let resp = ureq::get(&url.to_string())
|
||||||
|
.timeout(Duration::from_secs(10))
|
||||||
|
.call()?;
|
||||||
|
if let Some(val) = resp.header("Content-Length") {
|
||||||
|
if let Ok(num) = val.parse::<usize>() {
|
||||||
|
sender.send(Message::ContentLength(num))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let reader = resp.into_reader();
|
||||||
|
let mut reader = zstd::Decoder::new(reader)?;
|
||||||
|
let mut buf = vec![];
|
||||||
|
loop {
|
||||||
|
let bytes = reader.read(&mut buf)?;
|
||||||
|
if bytes == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sender.send(Message::BytesRead(bytes))?;
|
||||||
|
}
|
||||||
|
let packages = ron::de::from_bytes(&buf)?;
|
||||||
|
Ok(Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
base_url: url.clone(),
|
||||||
|
packages,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn search_names(&self, query: &str) -> Result<Vec<&Package>, PoisonError<Vec<&Package>>> {
|
pub fn search_names(&self, query: &str) -> Result<Vec<&Package>, PoisonError<Vec<&Package>>> {
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
if let Some(p) = self.packages.get(query) {
|
if let Some(p) = self.packages.get(query) {
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
use {
|
|
||||||
crate::Version,
|
|
||||||
chrono::{offset::Utc, DateTime},
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
std::{cmp::Ordering, error::Error, fmt, str::FromStr},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct GitRev {
|
|
||||||
/// the short revision hash
|
|
||||||
pub hash: String,
|
|
||||||
/// the time of the revision commit
|
|
||||||
pub datetime: DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for GitRev {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "git_{}.{}", self.hash, self.datetime.format("%Y%m%d"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for GitRev {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
self.datetime.partial_cmp(&other.datetime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for GitRev {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.datetime.cmp(&other.datetime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ParseGitRevError;
|
|
||||||
|
|
||||||
impl fmt::Display for ParseGitRevError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Error parsing git version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ParseGitRevError {}
|
|
||||||
|
|
||||||
impl From<chrono::ParseError> for ParseGitRevError {
|
|
||||||
fn from(_value: chrono::ParseError) -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for GitRev {
|
|
||||||
type Err = ParseGitRevError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
if let Some(gitrev) = s.strip_prefix("git_") {
|
|
||||||
if let Some((hash, date)) = gitrev.split_once('_') {
|
|
||||||
if hash.len() == 7 {
|
|
||||||
let datetime = DateTime::parse_from_str(date, "%Y%m%d")?;
|
|
||||||
return Ok(Self {
|
|
||||||
hash: hash.to_string(),
|
|
||||||
datetime: datetime.into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(ParseGitRevError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Version> for GitRev {
|
|
||||||
type Error = ParseGitRevError;
|
|
||||||
|
|
||||||
fn try_from(value: Version) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Version::Git(g) => Ok(g),
|
|
||||||
_ => Err(ParseGitRevError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
use {
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
std::{error::Error, fmt, str::FromStr},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod gitrev;
|
|
||||||
mod rapid;
|
|
||||||
mod semver;
|
|
||||||
|
|
||||||
pub use {gitrev::GitRev, rapid::Rapid, semver::SemVer};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum Version {
|
|
||||||
Number(u32),
|
|
||||||
Rapid(Rapid),
|
|
||||||
SemVer(SemVer),
|
|
||||||
Git(GitRev),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Version {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::SemVer(SemVer {
|
|
||||||
major: 0,
|
|
||||||
minor: 1,
|
|
||||||
patch: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Version {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Number(n) => write!(f, "{n}"),
|
|
||||||
Self::SemVer(s) => write!(f, "{s}"),
|
|
||||||
Self::Rapid(r) => write!(f, "{r}"),
|
|
||||||
Self::Git(g) => write!(f, "{g}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SemVer> for Version {
|
|
||||||
fn from(value: SemVer) -> Self {
|
|
||||||
Self::SemVer(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rapid> for Version {
|
|
||||||
fn from(value: Rapid) -> Self {
|
|
||||||
Self::Rapid(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GitRev> for Version {
|
|
||||||
fn from(value: GitRev) -> Self {
|
|
||||||
Self::Git(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for Version {
|
|
||||||
fn from(value: u32) -> Self {
|
|
||||||
Self::Number(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
(Self::Number(s), Self::Rapid(o)) => s.partial_cmp(o),
|
|
||||||
(Self::Number(s), Self::SemVer(o)) => s.partial_cmp(o),
|
|
||||||
(Self::Rapid(s), Self::Number(o)) => s.partial_cmp(o),
|
|
||||||
(Self::Rapid(s), Self::Rapid(o)) => s.partial_cmp(o),
|
|
||||||
(Self::Rapid(s), Self::SemVer(o)) => s.partial_cmp(o),
|
|
||||||
(Self::SemVer(s), Self::Number(o)) => s.partial_cmp(o),
|
|
||||||
(Self::SemVer(s), Self::Rapid(o)) => s.partial_cmp(o),
|
|
||||||
(Self::SemVer(s), Self::SemVer(o)) => s.partial_cmp(o),
|
|
||||||
(Self::Git(s), Self::Git(o)) => s.partial_cmp(o),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ParseVersionError;
|
|
||||||
|
|
||||||
impl fmt::Display for ParseVersionError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "error parsing version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ParseVersionError {}
|
|
||||||
|
|
||||||
impl FromStr for Version {
|
|
||||||
type Err = ParseVersionError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
if let Ok(v) = s.parse::<SemVer>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else if let Ok(v) = s.parse::<Rapid>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else if let Ok(v) = s.parse::<GitRev>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else if let Ok(v) = s.parse::<u32>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else {
|
|
||||||
Err(ParseVersionError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cmp_semver_rapid_gt() {
|
|
||||||
let sem = "1.42.1".parse::<SemVer>().unwrap();
|
|
||||||
let rpd = "1.42".parse::<Rapid>().unwrap();
|
|
||||||
assert!(sem > rpd);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cmp_semver_rapid_eq() {
|
|
||||||
let sem = "1.42.0".parse::<SemVer>().unwrap();
|
|
||||||
let rpd = "1.42".parse::<Rapid>().unwrap();
|
|
||||||
assert!(sem == rpd);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,206 +0,0 @@
|
|||||||
use {
|
|
||||||
chrono::{offset::Utc, DateTime},
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
std::{error::Error, fmt, str::FromStr},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod gitrev;
|
|
||||||
mod rapid;
|
|
||||||
mod semver;
|
|
||||||
|
|
||||||
pub use {gitrev::GitRev, rapid::Rapid, semver::SemVer};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum Version {
|
|
||||||
Number(u32),
|
|
||||||
Rapid {
|
|
||||||
major: u32,
|
|
||||||
minor: u32,
|
|
||||||
},
|
|
||||||
SemVer {
|
|
||||||
major: u32,
|
|
||||||
minor: u32,
|
|
||||||
patch: u32,
|
|
||||||
},
|
|
||||||
Git {
|
|
||||||
hash: String,
|
|
||||||
datetime: DateTime<Utc>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Version {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::SemVer {
|
|
||||||
major: 0,
|
|
||||||
minor: 1,
|
|
||||||
patch: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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::Rapid { major, minor } => {
|
|
||||||
let v = Rapid {
|
|
||||||
major: *major,
|
|
||||||
minor: *minor,
|
|
||||||
};
|
|
||||||
write!(f, "{v}")
|
|
||||||
}
|
|
||||||
Self::Git { hash, datetime } => {
|
|
||||||
let v = GitRev {
|
|
||||||
hash: hash.clone(),
|
|
||||||
datetime: *datetime,
|
|
||||||
};
|
|
||||||
write!(f, "{v}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SemVer> for Version {
|
|
||||||
fn from(value: SemVer) -> Self {
|
|
||||||
Self::SemVer {
|
|
||||||
major: value.major,
|
|
||||||
minor: value.minor,
|
|
||||||
patch: value.patch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rapid> for Version {
|
|
||||||
fn from(value: Rapid) -> Self {
|
|
||||||
Self::Rapid {
|
|
||||||
major: value.major,
|
|
||||||
minor: value.minor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GitRev> for Version {
|
|
||||||
fn from(value: GitRev) -> Self {
|
|
||||||
Self::Git {
|
|
||||||
hash: value.hash,
|
|
||||||
datetime: value.datetime,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for Version {
|
|
||||||
fn from(value: u32) -> Self {
|
|
||||||
Self::Number(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
(
|
|
||||||
Self::SemVer {
|
|
||||||
major,
|
|
||||||
minor,
|
|
||||||
patch,
|
|
||||||
},
|
|
||||||
Self::SemVer {
|
|
||||||
major: a,
|
|
||||||
minor: b,
|
|
||||||
patch: c,
|
|
||||||
},
|
|
||||||
) => (major, minor, patch).partial_cmp(&(a, b, c)),
|
|
||||||
(Self::Rapid { major, minor }, Self::Rapid { major: a, minor: b }) => {
|
|
||||||
(major, minor).partial_cmp(&(a, b))
|
|
||||||
}
|
|
||||||
(
|
|
||||||
Self::Git {
|
|
||||||
hash: _a,
|
|
||||||
datetime: b,
|
|
||||||
},
|
|
||||||
Self::Git {
|
|
||||||
hash: _c,
|
|
||||||
datetime: d,
|
|
||||||
},
|
|
||||||
) => b.partial_cmp(&d),
|
|
||||||
(
|
|
||||||
Self::SemVer {
|
|
||||||
major,
|
|
||||||
minor,
|
|
||||||
patch,
|
|
||||||
},
|
|
||||||
Self::Rapid { major: a, minor: b },
|
|
||||||
) => SemVer {
|
|
||||||
major: *major,
|
|
||||||
minor: *minor,
|
|
||||||
patch: *patch,
|
|
||||||
}
|
|
||||||
.partial_cmp(&Rapid {
|
|
||||||
major: *a,
|
|
||||||
minor: *b,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ParseVersionError;
|
|
||||||
|
|
||||||
impl fmt::Display for ParseVersionError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "error parsing version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ParseVersionError {}
|
|
||||||
|
|
||||||
impl FromStr for Version {
|
|
||||||
type Err = ParseVersionError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
if let Ok(v) = s.parse::<SemVer>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else if let Ok(v) = s.parse::<Rapid>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else if let Ok(v) = s.parse::<GitRev>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else if let Ok(v) = s.parse::<u32>() {
|
|
||||||
Ok(v.into())
|
|
||||||
} else {
|
|
||||||
Err(ParseVersionError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cmp_semver_rapid_gt() {
|
|
||||||
let sem = "1.42.1".parse::<SemVer>().unwrap();
|
|
||||||
let rpd = "1.42".parse::<Rapid>().unwrap();
|
|
||||||
assert!(sem > rpd);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cmp_semver_rapid_eq() {
|
|
||||||
let sem = "1.42.0".parse::<SemVer>().unwrap();
|
|
||||||
let rpd = "1.42".parse::<Rapid>().unwrap();
|
|
||||||
assert!(sem == rpd);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
use crate::SemVer;
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::Version,
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
std::{cmp::Ordering, error::Error, fmt, num::ParseIntError, str::FromStr},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct Rapid {
|
|
||||||
pub major: u32,
|
|
||||||
pub minor: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Rapid {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}.{}", self.major, self.minor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Rapid {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
match self.major.partial_cmp(&other.major) {
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Less),
|
|
||||||
Some(Ordering::Equal) => self.minor.partial_cmp(&other.minor),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Rapid {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
match self.major.cmp(&other.major) {
|
|
||||||
Ordering::Greater => Ordering::Greater,
|
|
||||||
Ordering::Less => Ordering::Less,
|
|
||||||
Ordering::Equal => self.minor.cmp(&other.minor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<u32> for Rapid {
|
|
||||||
fn eq(&self, other: &u32) -> bool {
|
|
||||||
self.major == *other && self.minor == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<u32> for Rapid {
|
|
||||||
fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
|
|
||||||
match self.major.partial_cmp(other) {
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Less),
|
|
||||||
None => None,
|
|
||||||
Some(Ordering::Equal) => {
|
|
||||||
if self.minor == 0 {
|
|
||||||
Some(Ordering::Equal)
|
|
||||||
} else {
|
|
||||||
Some(Ordering::Greater)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<SemVer> for Rapid {
|
|
||||||
fn eq(&self, other: &SemVer) -> bool {
|
|
||||||
other.eq(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<SemVer> for Rapid {
|
|
||||||
fn partial_cmp(&self, other: &SemVer) -> Option<Ordering> {
|
|
||||||
match other.partial_cmp(self) {
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Less),
|
|
||||||
Some(Ordering::Equal) => Some(Ordering::Equal),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Rapid> for u32 {
|
|
||||||
fn eq(&self, other: &Rapid) -> bool {
|
|
||||||
other.eq(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<Rapid> for u32 {
|
|
||||||
fn partial_cmp(&self, other: &Rapid) -> Option<Ordering> {
|
|
||||||
match other.partial_cmp(self) {
|
|
||||||
Some(Ordering::Equal) => Some(Ordering::Equal),
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Less),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct ParseRapidError;
|
|
||||||
|
|
||||||
impl fmt::Display for ParseRapidError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Error parsing Rapid version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ParseRapidError {}
|
|
||||||
|
|
||||||
impl From<ParseIntError> for ParseRapidError {
|
|
||||||
fn from(_value: ParseIntError) -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Rapid {
|
|
||||||
type Err = ParseRapidError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let split = s.split('.').collect::<Vec<_>>();
|
|
||||||
match split.len() {
|
|
||||||
2 => {
|
|
||||||
let major = split.first().unwrap().parse::<u32>()?;
|
|
||||||
let minor = split.get(1).unwrap().parse::<u32>()?;
|
|
||||||
Ok(Self { major, minor })
|
|
||||||
}
|
|
||||||
_ => Err(ParseRapidError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Version> for Rapid {
|
|
||||||
type Error = ParseRapidError;
|
|
||||||
|
|
||||||
fn try_from(value: Version) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Version::SemVer(s) => {
|
|
||||||
if s.patch == 0 {
|
|
||||||
Ok(Self {
|
|
||||||
major: s.major,
|
|
||||||
minor: s.minor,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(ParseRapidError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Version::Rapid(s) => Ok(s),
|
|
||||||
Version::Number(major) => Ok(Self { major, minor: 0 }),
|
|
||||||
Version::Git(_) => Err(ParseRapidError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_semver() {
|
|
||||||
assert_eq!(
|
|
||||||
"93.0".parse::<Rapid>(),
|
|
||||||
Ok(Rapid {
|
|
||||||
major: 93,
|
|
||||||
minor: 0,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,191 +0,0 @@
|
|||||||
use {
|
|
||||||
crate::{Rapid, Version},
|
|
||||||
serde::{Deserialize, Serialize},
|
|
||||||
std::{cmp::Ordering, error::Error, fmt, num::ParseIntError, str::FromStr},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct SemVer {
|
|
||||||
pub major: u32,
|
|
||||||
pub minor: u32,
|
|
||||||
pub patch: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for SemVer {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for SemVer {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
match self.major.partial_cmp(&other.major) {
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Less),
|
|
||||||
None => None,
|
|
||||||
Some(Ordering::Equal) => match self.minor.partial_cmp(&other.minor) {
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Less),
|
|
||||||
None => None,
|
|
||||||
Some(Ordering::Equal) => self.patch.partial_cmp(&other.patch),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for SemVer {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
match self.major.cmp(&other.major) {
|
|
||||||
Ordering::Greater => Ordering::Greater,
|
|
||||||
Ordering::Less => Ordering::Less,
|
|
||||||
Ordering::Equal => match self.minor.cmp(&other.minor) {
|
|
||||||
Ordering::Greater => Ordering::Greater,
|
|
||||||
Ordering::Less => Ordering::Less,
|
|
||||||
Ordering::Equal => self.patch.cmp(&other.patch),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Rapid> for SemVer {
|
|
||||||
fn eq(&self, other: &Rapid) -> bool {
|
|
||||||
self.major == other.major && self.minor == other.minor && self.patch == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<Rapid> for SemVer {
|
|
||||||
fn partial_cmp(&self, other: &Rapid) -> Option<std::cmp::Ordering> {
|
|
||||||
match self.major.partial_cmp(&other.major) {
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Less),
|
|
||||||
None => None,
|
|
||||||
Some(Ordering::Equal) => match self.minor.partial_cmp(&other.minor) {
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Less),
|
|
||||||
None => None,
|
|
||||||
Some(Ordering::Equal) => match self.patch {
|
|
||||||
0 => Some(Ordering::Equal),
|
|
||||||
_ => Some(Ordering::Greater),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<u32> for SemVer {
|
|
||||||
fn eq(&self, other: &u32) -> bool {
|
|
||||||
self.major == *other && self.minor == 0 && self.patch == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<u32> for SemVer {
|
|
||||||
fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
|
|
||||||
match self.major.cmp(other) {
|
|
||||||
Ordering::Greater => Some(Ordering::Greater),
|
|
||||||
Ordering::Less => Some(Ordering::Less),
|
|
||||||
Ordering::Equal => {
|
|
||||||
if self.minor == 0 && self.patch == 0 {
|
|
||||||
Some(Ordering::Equal)
|
|
||||||
} else {
|
|
||||||
Some(Ordering::Greater)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<SemVer> for u32 {
|
|
||||||
fn eq(&self, other: &SemVer) -> bool {
|
|
||||||
other.eq(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<SemVer> for u32 {
|
|
||||||
fn partial_cmp(&self, other: &SemVer) -> Option<Ordering> {
|
|
||||||
match other.partial_cmp(self) {
|
|
||||||
Some(Ordering::Less) => Some(Ordering::Greater),
|
|
||||||
Some(Ordering::Greater) => Some(Ordering::Less),
|
|
||||||
Some(Ordering::Equal) => Some(Ordering::Equal),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct ParseSemVerError;
|
|
||||||
|
|
||||||
impl fmt::Display for ParseSemVerError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Error parsing SemVer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ParseSemVerError {}
|
|
||||||
|
|
||||||
impl From<ParseIntError> for ParseSemVerError {
|
|
||||||
fn from(_value: ParseIntError) -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for SemVer {
|
|
||||||
type Err = ParseSemVerError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let split = s.split('.').collect::<Vec<_>>();
|
|
||||||
match split.len() {
|
|
||||||
3 => {
|
|
||||||
let major = split.first().unwrap().parse::<u32>()?;
|
|
||||||
let minor = split.get(1).unwrap().parse::<u32>()?;
|
|
||||||
let patch = split.get(2).unwrap().parse::<u32>()?;
|
|
||||||
Ok(Self {
|
|
||||||
major,
|
|
||||||
minor,
|
|
||||||
patch,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Err(ParseSemVerError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Version> for SemVer {
|
|
||||||
type Error = ParseSemVerError;
|
|
||||||
fn try_from(value: Version) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Version::SemVer(s) => Ok(SemVer {
|
|
||||||
major: s.major,
|
|
||||||
minor: s.minor,
|
|
||||||
patch: s.patch,
|
|
||||||
}),
|
|
||||||
Version::Rapid(r) => Ok(SemVer {
|
|
||||||
major: r.major,
|
|
||||||
minor: r.minor,
|
|
||||||
patch: 0,
|
|
||||||
}),
|
|
||||||
Version::Number(n) => Ok(Self {
|
|
||||||
major: n,
|
|
||||||
minor: 0,
|
|
||||||
patch: 0,
|
|
||||||
}),
|
|
||||||
Version::Git(_) => Err(ParseSemVerError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_semver() {
|
|
||||||
assert_eq!(
|
|
||||||
"1.0.3".parse::<SemVer>(),
|
|
||||||
Ok(SemVer {
|
|
||||||
major: 1,
|
|
||||||
minor: 0,
|
|
||||||
patch: 3
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "tar"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
license = "GPL-3.0-only"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
deku = { workspace = true }
|
|
||||||
libc = "0.2"
|
|
||||||
thiserror = "1.0.40"
|
|
@ -1,8 +0,0 @@
|
|||||||
## About
|
|
||||||
Derived originally from [minitar](https://github.com/genonullfree/minitar), this
|
|
||||||
crate implements basic functionality for creating and extracting tar archives. It
|
|
||||||
has been adapted to allow creation of a Node (Tar header + 512byte blocks of data)
|
|
||||||
from the raw data plus file metadata. This allows for better efficiency when it
|
|
||||||
is embedded into another application (such as a package manager), as the raw data
|
|
||||||
and metadata about each file can be extracted once and reused for purposes such
|
|
||||||
as generating checksums, getting file sizes and creating packing lists.
|
|
@ -1,22 +0,0 @@
|
|||||||
use std::{fmt, io, num::ParseIntError, str::Utf8Error};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("DekuError: {0}")]
|
|
||||||
Deku(#[from] deku::DekuError),
|
|
||||||
#[error("IoError: {0}")]
|
|
||||||
Io(#[from] io::Error),
|
|
||||||
#[error("Error in conversion of oct_to_dev")]
|
|
||||||
Utf8Error(#[from] Utf8Error),
|
|
||||||
#[error("Error in conversion of oct_to_dev")]
|
|
||||||
ParseIntError(#[from] ParseIntError),
|
|
||||||
#[error("End of tar")]
|
|
||||||
EndOfTar,
|
|
||||||
#[error("Invalid magic")]
|
|
||||||
InvalidMagic,
|
|
||||||
#[error("Invalid Checksum")]
|
|
||||||
InvalidChecksum,
|
|
||||||
#[error("Parse int failed")]
|
|
||||||
Parse(#[from] fmt::Error),
|
|
||||||
}
|
|
@ -1,441 +0,0 @@
|
|||||||
use crate::Error;
|
|
||||||
use deku::prelude::*;
|
|
||||||
use std::{
|
|
||||||
env,
|
|
||||||
ffi::CStr,
|
|
||||||
fmt::{self, Write},
|
|
||||||
fs::{self, Metadata},
|
|
||||||
io,
|
|
||||||
ops::Deref,
|
|
||||||
os::{linux::fs::MetadataExt, unix::fs::FileTypeExt},
|
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum FileType {
|
|
||||||
Normal = 0x30,
|
|
||||||
Hardlink = 0x31,
|
|
||||||
Symlink = 0x32,
|
|
||||||
Char = 0x33,
|
|
||||||
Block = 0x34,
|
|
||||||
Dir = 0x35,
|
|
||||||
FIFO = 0x36,
|
|
||||||
Unknown = 0x00,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<T> for FileType
|
|
||||||
where
|
|
||||||
T: Deref<Target = Metadata>,
|
|
||||||
{
|
|
||||||
fn from(meta: T) -> Self {
|
|
||||||
if meta.is_dir() {
|
|
||||||
return FileType::Dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_type = meta.file_type();
|
|
||||||
if file_type.is_fifo() {
|
|
||||||
return FileType::FIFO;
|
|
||||||
} else if file_type.is_char_device() {
|
|
||||||
return FileType::Char;
|
|
||||||
} else if file_type.is_block_device() {
|
|
||||||
return FileType::Block;
|
|
||||||
} else if file_type.is_fifo() {
|
|
||||||
return FileType::FIFO;
|
|
||||||
} else if file_type.is_symlink() {
|
|
||||||
return FileType::Symlink;
|
|
||||||
} else if file_type.is_file() {
|
|
||||||
return FileType::Normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileType::Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Owner {
|
|
||||||
pub uid: u32,
|
|
||||||
pub gid: u32,
|
|
||||||
pub username: String,
|
|
||||||
pub groupname: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Owner {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
uid: 0,
|
|
||||||
gid: 0,
|
|
||||||
username: "root".into(),
|
|
||||||
groupname: "root".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, DekuRead, DekuWrite)]
|
|
||||||
#[deku(endian = "little")]
|
|
||||||
pub struct Header {
|
|
||||||
pub(crate) fname: [u8; 100],
|
|
||||||
pub(crate) mode: [u8; 8],
|
|
||||||
pub(crate) uid: [u8; 8],
|
|
||||||
pub(crate) gid: [u8; 8],
|
|
||||||
pub(crate) size: [u8; 12],
|
|
||||||
pub(crate) mtime: [u8; 12],
|
|
||||||
pub(crate) header_checksum: [u8; 8],
|
|
||||||
pub(crate) link_indicator: [u8; 1],
|
|
||||||
pub(crate) link_name: [u8; 100],
|
|
||||||
pub(crate) ustar_magic: [u8; 6],
|
|
||||||
pub(crate) ustar_version: [u8; 2],
|
|
||||||
pub(crate) username: [u8; 32],
|
|
||||||
pub(crate) groupname: [u8; 32],
|
|
||||||
pub(crate) device_major: [u8; 8],
|
|
||||||
pub(crate) device_minor: [u8; 8],
|
|
||||||
pub(crate) file_prefix: [u8; 155],
|
|
||||||
pub(crate) reserved: [u8; 12],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Header {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
fname: [0; 100],
|
|
||||||
mode: [0; 8],
|
|
||||||
uid: [0; 8],
|
|
||||||
gid: [0; 8],
|
|
||||||
size: [0; 12],
|
|
||||||
mtime: [0; 12],
|
|
||||||
header_checksum: [0x20; 8],
|
|
||||||
link_indicator: [0; 1],
|
|
||||||
link_name: [0; 100],
|
|
||||||
ustar_magic: [0x75, 0x73, 0x74, 0x61, 0x72, 0x20],
|
|
||||||
ustar_version: [0x20, 0x00],
|
|
||||||
username: [0; 32],
|
|
||||||
groupname: [0; 32],
|
|
||||||
device_major: [0; 8],
|
|
||||||
device_minor: [0; 8],
|
|
||||||
file_prefix: [0; 155],
|
|
||||||
reserved: [0; 12],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Header {
|
|
||||||
pub fn filename(&self) -> Result<String, fmt::Error> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in self.fname {
|
|
||||||
if c != b'\0' {
|
|
||||||
write!(s, "{c}")?;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mode(&self) -> Result<u32, Error> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in self.mode {
|
|
||||||
if c != b'\0' {
|
|
||||||
write!(s, "{c}")?;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mode = u32::from_str_radix(&s, 8)?;
|
|
||||||
Ok(mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn uid(&self) -> Result<u32, Error> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in self.mode {
|
|
||||||
if c != b'\0' {
|
|
||||||
write!(s, "{c}")?;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let uid = u32::from_str_radix(&s, 8)?;
|
|
||||||
Ok(uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gid(&self) -> Result<u32, Error> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in self.mode {
|
|
||||||
if c != b'\0' {
|
|
||||||
write!(s, "{c}")?;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let gid = u32::from_str_radix(&s, 8)?;
|
|
||||||
Ok(gid)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn username(&self) -> Result<String, fmt::Error> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in self.username {
|
|
||||||
if c != b'\0' {
|
|
||||||
write!(s, "{c}")?;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn groupname(&self) -> Result<String, fmt::Error> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in self.groupname {
|
|
||||||
if c != b'\0' {
|
|
||||||
write!(s, "{c}")?;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn owner(&self) -> Result<Owner, Error> {
|
|
||||||
let uid = self.uid()?;
|
|
||||||
let gid = self.gid()?;
|
|
||||||
let username = self.username()?;
|
|
||||||
let groupname = self.groupname()?;
|
|
||||||
Ok(Owner {
|
|
||||||
uid,
|
|
||||||
gid,
|
|
||||||
username,
|
|
||||||
groupname,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prefix(&self) -> Result<String, fmt::Error> {
|
|
||||||
let mut s = String::new();
|
|
||||||
for c in self.file_prefix {
|
|
||||||
if c != b'\0' {
|
|
||||||
write!(s, "{c}")?;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(filename: &str) -> Result<Self, Error> {
|
|
||||||
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 path = PathBuf::from(&filename);
|
|
||||||
let name = match path.file_name().and_then(|n| n.to_str()) {
|
|
||||||
Some(n) => n.to_string(),
|
|
||||||
None => {
|
|
||||||
return Err(Error::Io(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"Cannot get file name",
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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())))
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Fill in metadata */
|
|
||||||
header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
|
|
||||||
let mode = format!("{:07o}", (meta.st_mode() & 0o777));
|
|
||||||
header.mode[..mode.len()].copy_from_slice(mode.as_bytes());
|
|
||||||
let user = format!("{:07o}", meta.st_uid());
|
|
||||||
header.uid[..user.len()].copy_from_slice(user.as_bytes());
|
|
||||||
let group = format!("{:07o}", meta.st_gid());
|
|
||||||
header.gid[..group.len()].copy_from_slice(group.as_bytes());
|
|
||||||
let size = format!("{:011o}", meta.st_size());
|
|
||||||
header.size[..size.len()].copy_from_slice(size.as_bytes());
|
|
||||||
let mtime = format!("{:011o}", meta.st_mtime());
|
|
||||||
header.mtime[..mtime.len()].copy_from_slice(mtime.as_bytes());
|
|
||||||
if let Some(prefix) = prefix {
|
|
||||||
header.file_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the file type and conditional metadata */
|
|
||||||
header.link_indicator[0] = FileType::from(&meta) as u8;
|
|
||||||
if header.link_indicator[0] == FileType::Symlink as u8 {
|
|
||||||
let link = fs::read_link(filename)?.to_str().unwrap().to_string();
|
|
||||||
header.link_name[..link.len()].copy_from_slice(link.as_bytes());
|
|
||||||
} else if header.link_indicator[0] == FileType::Block as u8 {
|
|
||||||
let major = format!("{:07o}", meta.st_dev());
|
|
||||||
header.device_major[..major.len()].copy_from_slice(major.as_bytes());
|
|
||||||
let minor = format!("{:07o}", meta.st_rdev());
|
|
||||||
header.device_minor[..minor.len()].copy_from_slice(minor.as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Find better way to get username */
|
|
||||||
let key = "USER";
|
|
||||||
if let Ok(val) = env::var(key) {
|
|
||||||
header.username[..val.len()].copy_from_slice(val.as_bytes())
|
|
||||||
}
|
|
||||||
/* TODO: Find way to get groupname */
|
|
||||||
|
|
||||||
/* Update the header checksum value */
|
|
||||||
header.update_checksum()?;
|
|
||||||
|
|
||||||
Ok(header)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_from_meta(
|
|
||||||
filename: &str,
|
|
||||||
meta: &Metadata,
|
|
||||||
owner: Option<Owner>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
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 path = PathBuf::from(&filename);
|
|
||||||
let name = match path.file_name().and_then(|n| n.to_str()) {
|
|
||||||
Some(n) => n.to_string(),
|
|
||||||
None => {
|
|
||||||
return Err(Error::Io(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"Cannot get file name",
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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())))
|
|
||||||
};
|
|
||||||
header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
|
|
||||||
let mode = format!("{:07o}", meta.st_mode());
|
|
||||||
header.mode[..mode.len()].copy_from_slice(mode.as_bytes());
|
|
||||||
let owner = match owner {
|
|
||||||
Some(o) => o,
|
|
||||||
None => Owner {
|
|
||||||
uid: meta.st_uid(),
|
|
||||||
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());
|
|
||||||
let gid = format!("{:07o}", owner.gid);
|
|
||||||
header.gid[..gid.len()].copy_from_slice(gid.as_bytes());
|
|
||||||
let size = format!("{:011o}", meta.len());
|
|
||||||
header.size[..size.len()].copy_from_slice(size.as_bytes());
|
|
||||||
let mtime = format!("{:011o}", meta.st_mtime());
|
|
||||||
header.mtime[..mtime.len()].copy_from_slice(mtime.as_bytes());
|
|
||||||
if let Some(prefix) = prefix {
|
|
||||||
header.file_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes());
|
|
||||||
}
|
|
||||||
header.link_indicator[0] = FileType::from(meta) as u8;
|
|
||||||
if header.link_indicator[0] == FileType::Symlink as u8 {
|
|
||||||
let link = fs::read_link(filename)?.to_str().unwrap().to_string();
|
|
||||||
header.link_name[..link.len()].copy_from_slice(link.as_bytes());
|
|
||||||
} else if header.link_indicator[0] == FileType::Block as u8 {
|
|
||||||
let major = format!("{:07o}", meta.st_dev());
|
|
||||||
header.device_major[..major.len()].copy_from_slice(major.as_bytes());
|
|
||||||
let minor = format!("{:07o}", meta.st_rdev());
|
|
||||||
header.device_minor[..minor.len()].copy_from_slice(minor.as_bytes());
|
|
||||||
}
|
|
||||||
header.username[..owner.username.len()].copy_from_slice(owner.username.as_bytes());
|
|
||||||
header.groupname[..owner.groupname.len()].copy_from_slice(owner.groupname.as_bytes());
|
|
||||||
header.update_checksum()?;
|
|
||||||
Ok(header)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that the magic value received matches the magic value required in the Tar specification.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tar::tar::Header;
|
|
||||||
/// let header = Header::default();
|
|
||||||
/// if !header.validate_magic() {
|
|
||||||
/// println!("Magic value is invalid");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn validate_magic(self) -> bool {
|
|
||||||
self.ustar_magic == "ustar ".as_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates the header checksum computes to the expected value.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tar::tar::Header;
|
|
||||||
/// let header = Header::default();
|
|
||||||
/// if header.validate_checksum().unwrap() {
|
|
||||||
/// println!("Checksum is valid");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn validate_checksum(self) -> Result<bool, Error> {
|
|
||||||
let mut test = self;
|
|
||||||
let mut new = [0x20u8; 8];
|
|
||||||
test.header_checksum.copy_from_slice(&[0x20; 8]);
|
|
||||||
|
|
||||||
let tmp = format!("{:06o}\x00", test.calc_checksum()?);
|
|
||||||
new[..tmp.len()].copy_from_slice(tmp.as_bytes());
|
|
||||||
|
|
||||||
Ok(self.header_checksum == new)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the header checksum value.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tar::tar::Header;
|
|
||||||
/// let mut header = Header::default();
|
|
||||||
///
|
|
||||||
/// /* Fill in header information */
|
|
||||||
///
|
|
||||||
/// header.update_checksum();
|
|
||||||
/// ```
|
|
||||||
pub fn update_checksum(&mut self) -> Result<(), Error> {
|
|
||||||
let checksum = format!("{:06o}\x00", self.calc_checksum()?);
|
|
||||||
self.header_checksum[..checksum.len()].copy_from_slice(checksum.as_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc_checksum(self) -> Result<usize, Error> {
|
|
||||||
let out = self.to_bytes()?;
|
|
||||||
let mut checksum = 0;
|
|
||||||
for i in out {
|
|
||||||
checksum += i as usize;
|
|
||||||
}
|
|
||||||
Ok(checksum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_username_for_uid<'a>(uid: u32) -> Result<&'a str, std::str::Utf8Error> {
|
|
||||||
let user = unsafe {
|
|
||||||
let pw = libc::getpwuid(uid);
|
|
||||||
let name = (*pw).pw_name;
|
|
||||||
CStr::from_ptr(name)
|
|
||||||
};
|
|
||||||
user.to_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub 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;
|
|
||||||
CStr::from_ptr(name)
|
|
||||||
};
|
|
||||||
group.to_str()
|
|
||||||
}
|
|
134
tar/src/lib.rs
134
tar/src/lib.rs
@ -1,134 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
io::{self, BufReader, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
mod header;
|
|
||||||
mod node;
|
|
||||||
pub use {
|
|
||||||
error::Error,
|
|
||||||
header::{FileType, Header, Owner},
|
|
||||||
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
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::fs::File;
|
|
||||||
/// use tar::tar::TarFile;
|
|
||||||
///
|
|
||||||
/// let data = Archive::new("test/1.txt".to_string()).unwrap();
|
|
||||||
///
|
|
||||||
/// let out = File::create("test/2.tar".to_string()).unwrap();
|
|
||||||
/// data.write(&out).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn write<T: io::Write + Copy>(self, mut input: T) -> Result<usize, Error> {
|
|
||||||
let mut written = 0;
|
|
||||||
for f in self.nodes.clone() {
|
|
||||||
written += f.write(input)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Complete the write with 18 blocks of 512 ``0x00`` bytes per the specification */
|
|
||||||
if !self.nodes.is_empty() {
|
|
||||||
input.write_all(&[0; 9216])?;
|
|
||||||
written += 9216;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(written)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new `TarFile` struct and initialize it with a `filename` file. This will read in the file to
|
|
||||||
/// the `TarFile` struct as a `TarNode`.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tar::Archive;
|
|
||||||
///
|
|
||||||
/// let data = Archive::new("test/1.txt".to_string()).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn new(filename: &str) -> Result<Self, Error> {
|
|
||||||
Ok(Self {
|
|
||||||
nodes: vec![Node::read_file_to_tar(filename)?],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append another file to the `TarFile.file` vector. This adds a file to the internal representation of the tar file.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tar::Archive;
|
|
||||||
///
|
|
||||||
/// let mut data = Archive::new("test/1.txt".to_string()).unwrap();
|
|
||||||
/// data.append("test/1.txt".to_string()).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn append(&mut self, filename: &str) -> Result<(), Error> {
|
|
||||||
self.nodes.push(Node::read_file_to_tar(filename)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open and load an external tar file into the internal `TarFile` struct. This parses and loads up all the files
|
|
||||||
/// contained within the external tar file.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tar::Archive;
|
|
||||||
///
|
|
||||||
/// Archive::open("test/1.tar".to_string()).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn open(filename: String) -> Result<Self, Error> {
|
|
||||||
let file = File::open(&filename)?;
|
|
||||||
let mut reader = BufReader::new(file);
|
|
||||||
let mut out = Self {
|
|
||||||
nodes: Vec::<Node>::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
while let Ok(t) = Node::read(&mut reader) {
|
|
||||||
out.nodes.push(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove the first file from the Tar that matches the filename and path.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use minitar::tar::TarFile;
|
|
||||||
///
|
|
||||||
/// let mut data = TarFile::new("test/1.tar".to_string()).unwrap();
|
|
||||||
/// data.remove("test/1.tar".to_string()).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn remove(&mut self, filename: String) -> Result<bool, Error> {
|
|
||||||
let mut name = [0u8; 100];
|
|
||||||
name[..filename.len()].copy_from_slice(filename.as_bytes());
|
|
||||||
if let Some(i) = &self.nodes.iter().position(|x| x.header.fname == name) {
|
|
||||||
self.nodes.remove(*i);
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
137
tar/src/node.rs
137
tar/src/node.rs
@ -1,137 +0,0 @@
|
|||||||
use crate::{header::Owner, Error, FileType, Header};
|
|
||||||
use deku::prelude::*;
|
|
||||||
use std::{
|
|
||||||
fs::{File, Metadata},
|
|
||||||
io::{self, BufReader},
|
|
||||||
str,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct Node {
|
|
||||||
pub header: Header,
|
|
||||||
pub data: Vec<[u8; 512]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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()?)?;
|
|
||||||
let mut written = 512;
|
|
||||||
for d in self.data {
|
|
||||||
input.write_all(&d)?;
|
|
||||||
written += d.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(written)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<Self, Error> {
|
|
||||||
let mut h = vec![0u8; 512];
|
|
||||||
input.read_exact(&mut h)?;
|
|
||||||
|
|
||||||
let (_, header) = Header::from_bytes((&h, 0))?;
|
|
||||||
if header == Header::default() {
|
|
||||||
return Err(Error::EndOfTar);
|
|
||||||
}
|
|
||||||
if !header.validate_magic() {
|
|
||||||
return Err(Error::InvalidMagic);
|
|
||||||
}
|
|
||||||
if !header.validate_checksum()? {
|
|
||||||
return Err(Error::InvalidChecksum);
|
|
||||||
}
|
|
||||||
|
|
||||||
let chunks = (oct_to_dec(&header.size)? / 512) + 1;
|
|
||||||
Ok(Node {
|
|
||||||
header,
|
|
||||||
data: Node::chunk_file(&mut input, Some(chunks))?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open and read a file from the ``filename`` argument to a TarNode.
|
|
||||||
pub fn read_file_to_tar(filename: &str) -> Result<Self, Error> {
|
|
||||||
let header = Header::new(filename)?;
|
|
||||||
if header.link_indicator[0] != FileType::Normal as u8 {
|
|
||||||
return Ok(Node {
|
|
||||||
header,
|
|
||||||
data: Vec::<[u8; 512]>::new(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = File::open(filename)?;
|
|
||||||
let mut reader = BufReader::new(file);
|
|
||||||
Ok(Node {
|
|
||||||
header,
|
|
||||||
data: Node::chunk_file(&mut reader, None)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<Self, Error> {
|
|
||||||
let header = Header::new_from_meta(filename, meta, owner)?;
|
|
||||||
if header.link_indicator[0] != FileType::Normal as u8 {
|
|
||||||
return Ok(Node {
|
|
||||||
header,
|
|
||||||
data: Vec::<[u8; 512]>::new(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let mut reader = BufReader::new(data);
|
|
||||||
Ok(Node {
|
|
||||||
header,
|
|
||||||
data: Node::chunk_file(&mut reader, None)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read in and split a file into ``512`` byte chunks.
|
|
||||||
fn chunk_file<T: std::io::Read>(
|
|
||||||
file: &mut T,
|
|
||||||
max_chunks: Option<usize>,
|
|
||||||
) -> Result<Vec<[u8; 512]>, Error> {
|
|
||||||
/* Extract the file data from the tar file */
|
|
||||||
let mut out = Vec::<[u8; 512]>::new();
|
|
||||||
let mut n = if let Some(max) = max_chunks {
|
|
||||||
max
|
|
||||||
} else {
|
|
||||||
usize::MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Carve out 512 bytes at a time */
|
|
||||||
let mut buf: [u8; 512] = [0; 512];
|
|
||||||
loop {
|
|
||||||
let len = file.read(&mut buf)?;
|
|
||||||
|
|
||||||
n -= 1;
|
|
||||||
|
|
||||||
/* If read len == 0, we've hit the EOF */
|
|
||||||
if len == 0 || n == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save this chunk */
|
|
||||||
out.push(buf);
|
|
||||||
}
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn oct_to_dec(input: &[u8]) -> Result<usize, Error> {
|
|
||||||
/* Convert the &[u8] to string and remove the null byte */
|
|
||||||
let mut s = str::from_utf8(input)?.to_string();
|
|
||||||
s.pop();
|
|
||||||
|
|
||||||
/* Convert to usize from octal */
|
|
||||||
Ok(usize::from_str_radix(&s, 8)?)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user