mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 05:25:14 +08:00
285 lines
9.7 KiB
Rust
285 lines
9.7 KiB
Rust
//! Official JP resource pull planning.
|
|
|
|
use bat_adapters::official::inventory::{
|
|
YostarJpDownloadInventory, YostarJpPlatformDownloadInventory,
|
|
};
|
|
use bat_adapters::official::yostar_jp::{
|
|
verified_official_platforms, PatchPlatform, YostarJpResourceDiscoveryPlan, YostarJpResourceRoot,
|
|
};
|
|
use std::collections::HashSet;
|
|
|
|
/// Full official JP pull plan, covering discovery endpoints and concrete
|
|
/// resource downloads.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct OfficialResourcePullPlan {
|
|
/// Seed URLs and selected root from server-info.
|
|
pub discovery: YostarJpResourceDiscoveryPlan,
|
|
/// Concrete file inventory extracted from the catalog bytes.
|
|
pub inventory: YostarJpPlatformDownloadInventory,
|
|
/// Platforms that will be pulled.
|
|
pub platforms: Vec<PatchPlatform>,
|
|
}
|
|
|
|
impl OfficialResourcePullPlan {
|
|
/// Creates a pull plan for the requested platforms.
|
|
pub fn new(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpDownloadInventory,
|
|
platforms: &[PatchPlatform],
|
|
) -> Self {
|
|
Self::new_platform_inventory(
|
|
discovery,
|
|
YostarJpPlatformDownloadInventory::from_shared_inventory(inventory, platforms),
|
|
platforms,
|
|
)
|
|
}
|
|
|
|
/// Creates a pull plan for the requested platforms using platform-specific
|
|
/// bundle/media catalogs.
|
|
pub fn new_platform_inventory(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpPlatformDownloadInventory,
|
|
platforms: &[PatchPlatform],
|
|
) -> Self {
|
|
Self {
|
|
discovery,
|
|
inventory,
|
|
platforms: unique_platforms(platforms),
|
|
}
|
|
}
|
|
|
|
/// Creates a pull plan for the verified official JP platforms.
|
|
pub fn verified(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpDownloadInventory,
|
|
) -> Self {
|
|
Self::new(discovery, inventory, &verified_official_platforms())
|
|
}
|
|
|
|
/// Creates a pull plan for the verified official JP platforms using
|
|
/// platform-specific bundle/media catalogs.
|
|
pub fn verified_platform_inventory(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpPlatformDownloadInventory,
|
|
) -> Self {
|
|
Self::new_platform_inventory(discovery, inventory, &verified_official_platforms())
|
|
}
|
|
|
|
/// Returns all discovery URLs in server-provided order.
|
|
pub fn discovery_urls(&self) -> Vec<String> {
|
|
self.discovery
|
|
.endpoints
|
|
.iter()
|
|
.map(|endpoint| endpoint.url.clone())
|
|
.collect()
|
|
}
|
|
|
|
/// Returns the validated official resource root used by the pull plan.
|
|
pub fn resource_root(&self) -> Result<YostarJpResourceRoot, String> {
|
|
YostarJpResourceRoot::from_addressables_root(&self.discovery.addressables_root)
|
|
}
|
|
|
|
/// Returns all concrete download URLs for the selected platforms.
|
|
pub fn content_urls(&self) -> Result<Vec<String>, String> {
|
|
let root = self.resource_root()?;
|
|
self.inventory
|
|
.direct_download_urls_for_platforms(&root, &self.platforms)
|
|
}
|
|
|
|
/// Returns discovery URLs followed by concrete content URLs.
|
|
pub fn all_urls(&self) -> Result<Vec<String>, String> {
|
|
let mut urls = self.discovery_urls();
|
|
let mut seen = urls.iter().cloned().collect::<HashSet<_>>();
|
|
|
|
for url in self.content_urls()? {
|
|
if seen.insert(url.clone()) {
|
|
urls.push(url);
|
|
}
|
|
}
|
|
|
|
Ok(urls)
|
|
}
|
|
}
|
|
|
|
/// Builds a pull plan for the verified official JP platforms.
|
|
pub fn build_official_pull_plan(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpDownloadInventory,
|
|
) -> OfficialResourcePullPlan {
|
|
OfficialResourcePullPlan::verified(discovery, inventory)
|
|
}
|
|
|
|
/// Builds a pull plan for the verified official JP platforms using
|
|
/// platform-specific bundle/media catalogs.
|
|
pub fn build_official_pull_plan_from_platform_inventory(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpPlatformDownloadInventory,
|
|
) -> OfficialResourcePullPlan {
|
|
OfficialResourcePullPlan::verified_platform_inventory(discovery, inventory)
|
|
}
|
|
|
|
/// Builds a pull plan for the requested platforms.
|
|
pub fn build_official_pull_plan_for_platforms(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpDownloadInventory,
|
|
platforms: &[PatchPlatform],
|
|
) -> OfficialResourcePullPlan {
|
|
OfficialResourcePullPlan::new(discovery, inventory, platforms)
|
|
}
|
|
|
|
/// Builds a pull plan for the requested platforms using platform-specific
|
|
/// bundle/media catalogs.
|
|
pub fn build_official_pull_plan_for_platform_inventory(
|
|
discovery: YostarJpResourceDiscoveryPlan,
|
|
inventory: YostarJpPlatformDownloadInventory,
|
|
platforms: &[PatchPlatform],
|
|
) -> OfficialResourcePullPlan {
|
|
OfficialResourcePullPlan::new_platform_inventory(discovery, inventory, platforms)
|
|
}
|
|
|
|
fn unique_platforms(platforms: &[PatchPlatform]) -> Vec<PatchPlatform> {
|
|
platforms
|
|
.iter()
|
|
.copied()
|
|
.fold(Vec::new(), |mut unique, platform| {
|
|
if !unique.contains(&platform) {
|
|
unique.push(platform);
|
|
}
|
|
unique
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use bat_adapters::official::inventory::{
|
|
YostarJpDownloadInventory, YostarJpPlatformCatalogInventory,
|
|
YostarJpPlatformDownloadInventory,
|
|
};
|
|
use bat_adapters::official::yostar_jp::{
|
|
YostarJpResourceEndpoint, YostarJpResourceEndpointKind,
|
|
};
|
|
|
|
fn discovery_plan() -> YostarJpResourceDiscoveryPlan {
|
|
YostarJpResourceDiscoveryPlan {
|
|
connection_group_name: "Prod-Audit".to_string(),
|
|
app_version: "1.70.0".to_string(),
|
|
bundle_version: Some("s8tloc7lo3".to_string()),
|
|
addressables_root:
|
|
"https://prod-clientpatch.bluearchiveyostar.com/r93_token".to_string(),
|
|
endpoints: vec![
|
|
YostarJpResourceEndpoint {
|
|
kind: YostarJpResourceEndpointKind::TableCatalog,
|
|
platform: None,
|
|
url: "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.bytes".to_string(),
|
|
},
|
|
YostarJpResourceEndpoint {
|
|
kind: YostarJpResourceEndpointKind::TableCatalogHash,
|
|
platform: None,
|
|
url: "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.hash".to_string(),
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
fn inventory() -> YostarJpDownloadInventory {
|
|
YostarJpDownloadInventory::from_catalog_bytes(
|
|
b"FullPatch_000.zip",
|
|
b"ExcelDB.db ExcelDB.db",
|
|
b"JP_Airi.zip",
|
|
)
|
|
}
|
|
|
|
fn platform_inventory() -> YostarJpPlatformDownloadInventory {
|
|
YostarJpPlatformDownloadInventory::from_catalog_bytes(
|
|
b"ExcelDB.db ExcelDB.db",
|
|
vec![
|
|
YostarJpPlatformCatalogInventory::from_catalog_bytes(
|
|
PatchPlatform::Windows,
|
|
b"FullPatch_000.zip",
|
|
b"JP_Airi_Win.zip",
|
|
),
|
|
YostarJpPlatformCatalogInventory::from_catalog_bytes(
|
|
PatchPlatform::Android,
|
|
b"FullPatch_001.zip",
|
|
b"JP_Airi_Android.zip",
|
|
),
|
|
],
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn builds_verified_pull_plan_with_all_urls() {
|
|
let plan = build_official_pull_plan(discovery_plan(), inventory());
|
|
|
|
assert_eq!(plan.platforms, verified_official_platforms());
|
|
assert_eq!(plan.discovery_urls().len(), 2);
|
|
|
|
let content_urls = plan.content_urls().unwrap();
|
|
assert!(content_urls
|
|
.iter()
|
|
.any(|url| url.contains("/Windows_PatchPack/FullPatch_000.zip")));
|
|
assert!(content_urls
|
|
.iter()
|
|
.any(|url| url.contains("/Android_PatchPack/FullPatch_000.zip")));
|
|
assert_eq!(content_urls.len(), 5);
|
|
|
|
let all_urls = plan.all_urls().unwrap();
|
|
assert_eq!(all_urls.len(), 7);
|
|
assert!(all_urls[0].ends_with("TableCatalog.bytes"));
|
|
assert!(all_urls
|
|
.iter()
|
|
.any(|url| url.ends_with("/MediaResources-Windows/JP_Airi.zip")));
|
|
assert_eq!(
|
|
all_urls
|
|
.iter()
|
|
.filter(|url| url.ends_with("/MediaResources/JP_Airi.zip"))
|
|
.count(),
|
|
1
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn deduplicates_requested_platforms() {
|
|
let plan = build_official_pull_plan_for_platforms(
|
|
discovery_plan(),
|
|
inventory(),
|
|
&[PatchPlatform::Windows, PatchPlatform::Windows],
|
|
);
|
|
|
|
assert_eq!(plan.platforms, vec![PatchPlatform::Windows]);
|
|
}
|
|
|
|
#[test]
|
|
fn verified_pull_plan_is_windows_and_android_only() {
|
|
let plan = build_official_pull_plan(discovery_plan(), inventory());
|
|
|
|
assert_eq!(
|
|
plan.platforms,
|
|
vec![PatchPlatform::Windows, PatchPlatform::Android]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn builds_platform_inventory_pull_plan_without_cross_mixing_catalogs() {
|
|
let plan = build_official_pull_plan_from_platform_inventory(
|
|
discovery_plan(),
|
|
platform_inventory(),
|
|
);
|
|
|
|
let all_urls = plan.all_urls().unwrap();
|
|
|
|
assert_eq!(plan.platforms, verified_official_platforms());
|
|
assert_eq!(all_urls.len(), 7);
|
|
assert!(all_urls
|
|
.iter()
|
|
.any(|url| url.ends_with("/Windows_PatchPack/FullPatch_000.zip")));
|
|
assert!(all_urls
|
|
.iter()
|
|
.any(|url| url.ends_with("/Android_PatchPack/FullPatch_001.zip")));
|
|
assert!(!all_urls
|
|
.iter()
|
|
.any(|url| url.ends_with("/Android_PatchPack/FullPatch_000.zip")));
|
|
}
|
|
}
|