OneLauncher-Cracked/packages/oneclient_app/src/view/onboarding/selection.rs
Julian Chang 668737fc6c
Rewrite bundles page
Fixes #547, #576
2026-07-15 16:24:52 -04:00

220 lines
6.6 KiB
Rust

use std::collections::HashSet;
use oneclient_core::{BundleArchive, BundleFile};
use crate::hooks::ClusterBundles;
pub fn pkg_key(cluster_id: i64, bundle_name: &str, package_id: &str) -> String {
format!("{cluster_id}|{bundle_name}|{package_id}")
}
pub fn is_default_bundle(archive: &BundleArchive) -> bool {
archive.manifest.enabled
}
pub fn is_default_file(file: &BundleFile) -> bool {
file.enabled && !file.hidden
}
pub fn is_optional_file(file: &BundleFile) -> bool {
!file.enabled && !file.hidden
}
pub fn default_selection(
items: &[ClusterBundles],
opted_in_categories: Option<&[String]>,
) -> HashSet<String> {
let mut selected = HashSet::new();
for cb in items {
for archive in &cb.archives {
if !is_default_bundle(archive) && !category_opted_in(archive, opted_in_categories) {
continue;
}
for file in &archive.manifest.files {
if is_default_file(file) {
selected.insert(pkg_key(
cb.cluster.id,
&archive.manifest.name,
&file.kind.package_id(),
));
}
}
}
}
selected
}
fn category_opted_in(archive: &BundleArchive, opted_in: Option<&[String]>) -> bool {
let Some(categories) = opted_in else {
return false;
};
let category = archive.manifest.category.trim();
if category.is_empty() {
return false;
}
categories.iter().any(|c| c.eq_ignore_ascii_case(category))
}
pub fn archive_selected(
cluster_id: i64,
archive: &BundleArchive,
selected: &HashSet<String>,
) -> bool {
let mut any = false;
for file in archive.manifest.files.iter().filter(|f| is_default_file(f)) {
any = true;
if !selected.contains(&pkg_key(
cluster_id,
&archive.manifest.name,
&file.kind.package_id(),
)) {
return false;
}
}
any
}
pub fn set_archive_selected(
cluster_id: i64,
archive: &BundleArchive,
on: bool,
selected: &mut HashSet<String>,
) {
for file in archive.manifest.files.iter().filter(|f| is_default_file(f)) {
let key = pkg_key(
cluster_id,
&archive.manifest.name,
&file.kind.package_id(),
);
if on {
selected.insert(key);
} else {
selected.remove(&key);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::view::onboarding::test_support::{archive, cluster, file};
fn catalog() -> Vec<ClusterBundles> {
vec![ClusterBundles {
cluster: cluster(1),
archives: vec![
archive("QoL", true, vec![file("qol-a", true, false)]),
archive(
"Utility",
true,
vec![file("util-a", true, false), file("util-opt", false, false)],
),
archive("PvP", false, vec![file("pvp-a", true, false)]),
archive("SkyBlock", false, vec![file("sb-a", true, false)]),
],
}]
}
#[test]
fn opt_in_bundles_are_not_selected_by_default() {
let selected = default_selection(&catalog(), None);
assert!(!selected.iter().any(|k| k.contains("[PvP]")));
assert!(!selected.iter().any(|k| k.contains("[SkyBlock]")));
}
#[test]
fn default_bundles_are_selected_by_default() {
let selected = default_selection(&catalog(), None);
assert!(selected.contains(&pkg_key(1, "OneClient 1.21.11 Fabric [QoL]", "qol-a")));
assert!(selected.contains(&pkg_key(1, "OneClient 1.21.11 Fabric [Utility]", "util-a")));
}
#[test]
fn files_the_bundle_ships_off_are_not_selected_by_default() {
let selected = default_selection(&catalog(), None);
assert!(!selected.contains(&pkg_key(
1,
"OneClient 1.21.11 Fabric [Utility]",
"util-opt"
)));
}
#[test]
fn migrated_categories_opt_into_their_bundles_without_losing_defaults() {
let cats = vec!["SkyBlock".to_string()];
let selected = default_selection(&catalog(), Some(&cats));
assert!(selected.contains(&pkg_key(1, "OneClient 1.21.11 Fabric [SkyBlock]", "sb-a")));
assert!(selected.contains(&pkg_key(1, "OneClient 1.21.11 Fabric [QoL]", "qol-a")));
assert!(!selected.iter().any(|k| k.contains("[PvP]")));
}
#[test]
fn migrated_category_match_ignores_case() {
let cats = vec!["skyblock".to_string()];
let selected = default_selection(&catalog(), Some(&cats));
assert!(selected.contains(&pkg_key(1, "OneClient 1.21.11 Fabric [SkyBlock]", "sb-a")));
}
#[test]
fn hidden_files_are_never_selected() {
let items = vec![ClusterBundles {
cluster: cluster(1),
archives: vec![archive(
"QoL",
true,
vec![file("shown", true, false), file("dep", true, true)],
)],
}];
let selected = default_selection(&items, None);
assert!(selected.contains(&pkg_key(1, "OneClient 1.21.11 Fabric [QoL]", "shown")));
assert!(!selected.contains(&pkg_key(1, "OneClient 1.21.11 Fabric [QoL]", "dep")));
}
#[test]
fn selection_is_per_cluster() {
let items = vec![
ClusterBundles {
cluster: cluster(1),
archives: vec![archive("SkyBlock", false, vec![file("sb-a", true, false)])],
},
ClusterBundles {
cluster: cluster(2),
archives: vec![archive("SkyBlock", false, vec![file("sb-a", true, false)])],
},
];
let mut selected = HashSet::new();
let sb = &items[0].archives[0];
set_archive_selected(1, sb, true, &mut selected);
assert!(archive_selected(1, sb, &selected));
assert!(!archive_selected(2, &items[1].archives[0], &selected));
}
#[test]
fn set_archive_selected_round_trips() {
let items = catalog();
let pvp = &items[0].archives[2];
let mut selected = HashSet::new();
set_archive_selected(1, pvp, true, &mut selected);
assert!(archive_selected(1, pvp, &selected));
set_archive_selected(1, pvp, false, &mut selected);
assert!(!archive_selected(1, pvp, &selected));
assert!(selected.is_empty());
}
#[test]
fn archive_with_no_default_files_is_not_selected() {
let a = archive("Odd", true, vec![file("opt", false, false)]);
assert!(!archive_selected(1, &a, &HashSet::new()));
}
}