This commit is contained in:
2022-05-26 11:16:29 +00:00
committed by GitHub
parent c002f124bb
commit 194f964e91
8 changed files with 233 additions and 2 deletions

28
Cargo.lock generated
View File

@@ -935,6 +935,27 @@ dependencies = [
"winapi",
]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "native-tls"
version = "0.2.8"
@@ -1142,6 +1163,7 @@ dependencies = [
"json",
"lazy_static",
"link-cplusplus",
"modular-bitfield",
"proc_macros",
"regex",
"reqwest",
@@ -1470,6 +1492,12 @@ dependencies = [
"pin-utils",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"

View File

@@ -20,6 +20,7 @@ indicatif = "0.17.0-rc.11"
int-enum = "0.4"
json = "0.12"
lazy_static = "1.4"
modular-bitfield = "0.11"
proc_macros = { path = "proc_macros" }
regex = "1"
reqwest = { version = "0.11", features = ["brotli", "deflate", "gzip", "rustls-tls", "socks", "stream"] }

View File

@@ -1,5 +1,7 @@
use crate::downloader::pd_file::file::PdFile;
use int_enum::IntEnum;
use modular_bitfield::BitfieldSpecifier;
use std::fmt::Display;
/// The status of the downloaded file.
#[repr(u8)]
@@ -59,8 +61,52 @@ impl PdFileType {
}
}
#[repr(u8)]
#[derive(BitfieldSpecifier, Clone, Copy, Debug, Eq, PartialEq, IntEnum)]
#[bits = 2]
/// The status of the each part in pd file.
pub enum PdFilePartStatus {
/// The download of this part is waited.
Waited = 0,
/// The download of this part is started.
Downloading = 1,
/// The download of this part is completed.
Downloaded = 2,
}
impl PdFilePartStatus {
#[inline]
/// Returns true if the download is waited
pub fn is_waited(&self) -> bool {
*self == Self::Waited
}
#[inline]
/// Returns true if the download is started
pub fn is_downloading(&self) -> bool {
*self == Self::Downloading
}
#[inline]
/// Returns true if the download is completed.
pub fn is_downloaded(&self) -> bool {
*self == Self::Downloaded
}
}
impl Display for PdFilePartStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Waited => { f.write_str("PdFilePartStatus::Waited") }
Self::Downloading => { f.write_str("PdFilePartStatus::Downloading") }
Self::Downloaded => { f.write_str("PdFilePartStatus::Downloaded") }
}
}
}
#[test]
fn test_enums() {
assert_eq!(PdFileStatus::Downloading.int_value().to_le_bytes(), [1]);
assert_eq!(PdFileType::MultiThread.int_value().to_le_bytes(), [1]);
assert_eq!(PdFilePartStatus::Downloaded.int_value().to_le_bytes(), [2]);
}

View File

@@ -1,3 +1,4 @@
use super::part_status::OutOfBoundsError;
use crate::gettext;
use int_enum::IntEnum;
use int_enum::IntEnumError;
@@ -5,6 +6,7 @@ use std::convert::From;
use std::fmt::Display;
use std::string::FromUtf8Error;
/// Pd file's error
#[derive(Debug, derive_more::From)]
pub enum PdFileError {
IoError(std::io::Error),
@@ -50,3 +52,9 @@ impl<T: IntEnum> From<IntEnumError<T>> for PdFileError {
Self::String(format!("{} {}", gettext("Invalid pd file: "), e))
}
}
impl<T: Display> From<OutOfBoundsError<T>> for PdFileError {
fn from(e: OutOfBoundsError<T>) -> Self {
Self::String(format!("{}", e))
}
}

View File

@@ -2,6 +2,7 @@ use crate::downloader::pd_file::error::PdFileError;
use crate::downloader::pd_file::enums::PdFileResult;
use crate::downloader::pd_file::enums::PdFileStatus;
use crate::downloader::pd_file::enums::PdFileType;
use crate::downloader::pd_file::part_status::PdFilePartStatus;
use crate::downloader::pd_file::version::PdFileVersion;
use crate::ext::io::StructRead;
use crate::ext::replace::ReplaceWith2;
@@ -24,6 +25,7 @@ use std::io::Write;
use std::ops::Drop;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::RwLock;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU32;
@@ -60,6 +62,8 @@ pub struct PdFile {
part_size: AtomicU32,
/// Only stored in memory.
mem_only: AtomicBool,
/// The status of the each part.
part_datas: RwLock<Vec<Arc<PdFilePartStatus>>>,
}
impl PdFile {
@@ -77,6 +81,7 @@ impl PdFile {
downloaded_file_size: AtomicU64::new(0),
part_size: AtomicU32::new(0),
mem_only: AtomicBool::new(true),
part_datas: RwLock::new(Vec::new()),
}
}
@@ -106,6 +111,17 @@ impl PdFile {
self.downloaded_file_size.load(Ordering::Relaxed)
}
/// Return status data of a part
/// * `index` - The part index
pub fn get_part_data(&self, index: usize) -> Option<Arc<PdFilePartStatus>> {
let datas = self.part_datas.get_ref();
if index < datas.len() {
Some(Arc::clone(&datas[index]))
} else {
None
}
}
#[inline]
/// Returns true if the download is completed.
pub fn is_completed(&self) -> bool {
@@ -198,6 +214,8 @@ impl PdFile {
downloaded_file_size: AtomicU64::new(downloaded_file_size),
part_size: AtomicU32::new(part_size),
mem_only: AtomicBool::new(false),
// #TODO
part_datas: RwLock::new(Vec::new()),
})
}

View File

@@ -1,8 +1,11 @@
/// The enums of the pd file.
pub mod enums;
/// The error type of the pd file.
pub mod error;
/// The pd file
pub mod file;
/// The enums of the pd file.
pub mod enums;
/// The status of the each part.
#[allow(dead_code)]
pub mod part_status;
/// Version of the pd file
pub mod version;

View File

@@ -0,0 +1,126 @@
use crate::downloader::pd_file::enums::PdFilePartStatus as PdFilePartStatus2;
use crate::downloader::pd_file::error::PdFileError;
use crate::ext::rw_lock::GetRwLock;
use crate::gettext;
use int_enum::IntEnum;
use modular_bitfield::bitfield;
use modular_bitfield::prelude::B30;
use std::fmt::Debug;
use std::fmt::Display;
use std::sync::RwLock;
/// The data is out of bounds.
#[derive(Clone, Debug)]
pub struct OutOfBoundsError<T> {
/// Type name
t: String,
/// the value
v: T,
}
impl<T> OutOfBoundsError<T> {
pub fn new<S: AsRef<str> + ?Sized>(t: &S, v: T) -> Self {
Self { t: String::from(t.as_ref()), v: v }
}
}
impl<T: Display> Display for OutOfBoundsError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("Failed to set type {} with value {}", self.t.as_str(), self.v))
}
}
#[bitfield(bits = 32)]
#[derive(Clone)]
/// The status of the each part in pd file (For internal usage)
struct PdFilePartStatusInternal {
#[bits = 2]
status: PdFilePartStatus2,
downloaded_size: B30,
}
impl Debug for PdFilePartStatusInternal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{{ status: {:?}, downloaded_size: {:?} }}", self.status(), self.downloaded_size()))
}
}
#[derive(Debug)]
/// The status of the each part in pd file
pub struct PdFilePartStatus {
status: RwLock<PdFilePartStatusInternal>,
}
impl PdFilePartStatus {
/// Create a new [PdFilePartStatus]
/// # paincs
/// Will panic if internal errors happened.
pub fn new() -> Self {
let mut status = PdFilePartStatusInternal::new();
status.set_status(PdFilePartStatus2::Waited);
status.set_downloaded_size(0);
Self { status: RwLock::new(status) }
}
/// Returns the status of this part
pub fn status(&self) -> PdFilePartStatus2 {
self.status.get_ref().status()
}
/// Set the status of this part
pub fn set_status(&self, status: PdFilePartStatus2) -> Result<(), PdFileError> {
match self.status.get_mut().set_status_checked(status) {
Ok(_) => { Ok(()) }
Err(_) => {
Err(PdFileError::from(OutOfBoundsError::new("PdFilePartStatus", status)))
}
}
}
#[inline]
/// Returns true if the download is waited
pub fn is_waited(&self) -> bool {
self.status().is_waited()
}
#[inline]
/// Returns true if the download is started
pub fn is_downloading(&self) -> bool {
self.status().is_downloading()
}
#[inline]
/// Returns true if the download is completed.
pub fn is_downloaded(&self) -> bool {
self.status().is_downloaded()
}
/// Create a new instance of the [PdFilePartStatus] from bytes.
/// * `bytes` - The data
/// * `offset` - The offset of the needed data
///
/// Returns a new instance if succeed otherwise a Error because the data is less than 4 bytes.
/// # Panics
/// Will panic if unwanted error occured
pub fn from_bytes<T: AsRef<[u8]> + ?Sized>(bytes: &T, offset: usize) -> Result<Self, PdFileError> {
let value = bytes.as_ref();
if (value.len() - offset) < 4 {
Err(gettext("At least 4 bytes is needed."))?;
}
let st = (value[offset] & 0xC0) / 0x20;
let mut status = PdFilePartStatusInternal::new();
status.set_status(PdFilePartStatus2::from_int(st)?);
// #TODO
Ok(Self { status: RwLock::new(status) })
}
}
#[test]
fn test_part_status() {
assert_eq!(std::mem::size_of::<PdFilePartStatusInternal>(), 4);
let status = PdFilePartStatus::new();
assert_eq!(status.status(), PdFilePartStatus2::Waited);
assert_eq!(status.is_waited(), true);
let status = PdFilePartStatus::from_bytes(&[80u8, 0, 0, 0], 0).unwrap();
assert_eq!(status.is_downloaded(), true);
}

View File

@@ -14,6 +14,7 @@ extern crate int_enum;
extern crate lazy_static;
#[cfg(all(feature = "link-cplusplus", target_env = "gnu"))]
extern crate link_cplusplus;
extern crate modular_bitfield;
extern crate proc_macros;
extern crate tokio;
extern crate regex;