mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 05:55:15 +08:00
feat: add official sync progress logging
This commit is contained in:
@@ -24,6 +24,88 @@ pub enum OfficialResourcePullStatus {
|
||||
Downloaded,
|
||||
}
|
||||
|
||||
impl OfficialResourcePullStatus {
|
||||
/// Returns a stable label for reports and progress logs.
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::SkippedExisting => "skipped_existing",
|
||||
Self::Resumed => "resumed",
|
||||
Self::Downloaded => "downloaded",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Progress event kind emitted while executing an official pull plan.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OfficialResourcePullProgressKind {
|
||||
/// A URL is about to be checked or downloaded.
|
||||
Started,
|
||||
/// A URL finished as skipped, resumed, or downloaded.
|
||||
Finished,
|
||||
}
|
||||
|
||||
impl OfficialResourcePullProgressKind {
|
||||
/// Returns a stable label for progress logs.
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Started => "started",
|
||||
Self::Finished => "finished",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Progress for one URL in an official pull plan.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OfficialResourcePullProgress {
|
||||
/// Progress event kind.
|
||||
pub kind: OfficialResourcePullProgressKind,
|
||||
/// One-based URL index in the pull plan.
|
||||
pub index: usize,
|
||||
/// Total URL count in the pull plan.
|
||||
pub total: usize,
|
||||
/// Official URL currently being processed.
|
||||
pub url: String,
|
||||
/// Finished pull status, when `kind` is `Finished`.
|
||||
pub status: Option<OfficialResourcePullStatus>,
|
||||
/// Final local file size, when `kind` is `Finished`.
|
||||
pub bytes: Option<u64>,
|
||||
/// Bytes transferred during this run, when `kind` is `Finished`.
|
||||
pub transferred_bytes: Option<u64>,
|
||||
}
|
||||
|
||||
impl OfficialResourcePullProgress {
|
||||
fn started(index: usize, total: usize, url: String) -> Self {
|
||||
Self {
|
||||
kind: OfficialResourcePullProgressKind::Started,
|
||||
index,
|
||||
total,
|
||||
url,
|
||||
status: None,
|
||||
bytes: None,
|
||||
transferred_bytes: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn finished(
|
||||
index: usize,
|
||||
total: usize,
|
||||
url: String,
|
||||
status: OfficialResourcePullStatus,
|
||||
bytes: u64,
|
||||
transferred_bytes: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
kind: OfficialResourcePullProgressKind::Finished,
|
||||
index,
|
||||
total,
|
||||
url,
|
||||
status: Some(status),
|
||||
bytes: Some(bytes),
|
||||
transferred_bytes: Some(transferred_bytes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Official hash algorithm used by a verified resource sidecar.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OfficialResourceHashAlgorithm {
|
||||
@@ -240,6 +322,16 @@ impl OfficialResourcePullService {
|
||||
pub fn pull(
|
||||
&self,
|
||||
plan: &OfficialResourcePullPlan,
|
||||
) -> Result<OfficialResourcePullReport, String> {
|
||||
self.pull_with_progress(plan, |_| {})
|
||||
}
|
||||
|
||||
/// Executes the plan and calls `progress` for every URL before and after it
|
||||
/// is checked or downloaded.
|
||||
pub fn pull_with_progress(
|
||||
&self,
|
||||
plan: &OfficialResourcePullPlan,
|
||||
mut progress: impl FnMut(OfficialResourcePullProgress),
|
||||
) -> Result<OfficialResourcePullReport, String> {
|
||||
fs::create_dir_all(&self.output_root).map_err(|error| {
|
||||
format!(
|
||||
@@ -251,8 +343,17 @@ impl OfficialResourcePullService {
|
||||
let official_hash_pairs = official_seed_hash_pairs(plan);
|
||||
let force_refresh_urls = official_hash_refresh_urls(&official_hash_pairs);
|
||||
let mut manifest = self.read_download_manifest()?;
|
||||
let urls = plan.all_urls()?;
|
||||
let total = urls.len();
|
||||
let mut items = Vec::new();
|
||||
for url in plan.all_urls()? {
|
||||
for (offset, url) in urls.into_iter().enumerate() {
|
||||
let index = offset + 1;
|
||||
progress(OfficialResourcePullProgress::started(
|
||||
index,
|
||||
total,
|
||||
url.clone(),
|
||||
));
|
||||
|
||||
if !is_official_yostar_jp_url(&url) {
|
||||
return Err(format!("Refusing to download non-official URL: {url}"));
|
||||
}
|
||||
@@ -282,6 +383,14 @@ impl OfficialResourcePullService {
|
||||
result
|
||||
};
|
||||
|
||||
progress(OfficialResourcePullProgress::finished(
|
||||
index,
|
||||
total,
|
||||
url.clone(),
|
||||
result.status,
|
||||
result.bytes,
|
||||
result.transferred_bytes,
|
||||
));
|
||||
items.push(OfficialResourcePullItem {
|
||||
url,
|
||||
destination,
|
||||
@@ -1610,6 +1719,41 @@ fi
|
||||
assert!(manifest.entries.contains_key(&report.items[0].url));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pull_emits_started_and_finished_progress_events() {
|
||||
let out_dir = TempDir::new().unwrap();
|
||||
let bin_dir = TempDir::new().unwrap();
|
||||
let curl_path = bin_dir.path().join("curl");
|
||||
write_fake_curl(&curl_path);
|
||||
|
||||
let service = OfficialResourcePullService::with_curl_command(out_dir.path(), &curl_path);
|
||||
let plan = build_official_pull_plan_for_platforms(
|
||||
discovery_plan(),
|
||||
inventory(),
|
||||
&[PatchPlatform::Windows],
|
||||
);
|
||||
let expected_urls = plan.all_urls().unwrap().len();
|
||||
let mut events = Vec::new();
|
||||
|
||||
let report = service
|
||||
.pull_with_progress(&plan, |event| events.push(event))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(report.items.len(), expected_urls);
|
||||
assert_eq!(events.len(), expected_urls * 2);
|
||||
assert_eq!(events[0].kind, OfficialResourcePullProgressKind::Started);
|
||||
assert_eq!(events[0].index, 1);
|
||||
assert_eq!(events[0].total, expected_urls);
|
||||
assert_eq!(events[1].kind, OfficialResourcePullProgressKind::Finished);
|
||||
assert_eq!(
|
||||
events[1].status,
|
||||
Some(OfficialResourcePullStatus::Downloaded)
|
||||
);
|
||||
assert!(events
|
||||
.iter()
|
||||
.any(|event| event.url.ends_with("/TableBundles/ExcelDB.db")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retries_transient_download_failures() {
|
||||
let out_dir = TempDir::new().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user