feat: manual migration in both directions, only when there's something to copy
This commit is contained in:
parent
0b78f16e9a
commit
0e98be3d5a
9 changed files with 187 additions and 79 deletions
|
|
@ -512,12 +512,14 @@ impl Actions {
|
|||
|
||||
pub fn open_manual_migration(&self, target_cluster_id: i64, source_cluster_id: i64) {
|
||||
self.set_release_migration_checking(target_cluster_id, true);
|
||||
self.set_release_migration_checking(source_cluster_id, true);
|
||||
let actions = self.clone();
|
||||
spawn_forever(async move {
|
||||
actions
|
||||
.run_manual_migration(target_cluster_id, source_cluster_id)
|
||||
.await;
|
||||
actions.set_release_migration_checking(target_cluster_id, false);
|
||||
actions.set_release_migration_checking(source_cluster_id, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ mod shortcut_actions;
|
|||
mod view_state;
|
||||
|
||||
pub use debounce::use_debounced;
|
||||
pub use selection::{Selection, use_selection};
|
||||
pub use overlay_claims::{
|
||||
OverlayClaims, use_overlay_claim, use_overlay_claim_when, use_overlay_claims,
|
||||
use_provide_overlay_claims,
|
||||
};
|
||||
pub use selection::{Selection, use_selection};
|
||||
pub use view_state::{PersistedView, use_view_state};
|
||||
|
||||
pub use active_cluster::{
|
||||
|
|
@ -34,44 +34,42 @@ pub use queries::{
|
|||
CachedImageQuery, CancelMicrosoftLoginKeys, CancelMicrosoftLoginMutation, ClusterAction,
|
||||
ClusterBundles, ClusterLogsQuery, ClusterMutation, DisableWarningsQuery, DiscardLeftoversKeys,
|
||||
DiscardLeftoversMutation, FinishMicrosoftLoginMutation, GameVersion, JavaMajorsQuery,
|
||||
LeftoversQuery, LogAction, LogContentQuery, MigrationQuery,
|
||||
OnboardingBundlesQuery, RefreshAccountKeys, RemoveAccountKeys, ScreenshotAction,
|
||||
SetDefaultAccountKeys, StorageAction, StorageActionMutation, StorageReportQuery, TermsQuery,
|
||||
UploadLogKeys, UploadLogMutation, UseDiscardLeftovers, UseLogAction, UseRefreshAccount,
|
||||
UseRemoveAccount, UseScreenshotAction, UseSetDefaultAccount, UseStorageAction, UseUploadLog,
|
||||
VERSIONS_PAGE_SIZE, accounts_have_microsoft, available_bundles, bundle_overrides_map,
|
||||
bundles_with_status_items, category_list, changelog_error, changelog_groups,
|
||||
changelog_is_loading, cluster_content_items, content_type_for_slug, disable_warnings,
|
||||
game_versions, has_migration_data, invalidate_cluster_content_queries,
|
||||
invalidate_cluster_queries, invalidate_java_queries, invalidate_leftovers_queries,
|
||||
invalidate_logs_queries, invalidate_profile_queries, invalidate_screenshots_queries,
|
||||
invalidate_storage_queries, java_majors, java_runtimes, latest_changelog_version, loaded_image,
|
||||
loader_versions, login_code_already_handled, migration_detection,
|
||||
mutation_error, mutation_is_pending, mutation_is_running, mutation_ok,
|
||||
onboarding_bundles_items, package_meta_batch, package_updates, pick_version_metadata,
|
||||
project_detail, provider_versions, query_error, query_is_busy, query_is_loading,
|
||||
cluster_art_url, refresh_version_art_gallery, reset_login_code_dedup, resolve_art_url, search_items,
|
||||
search_pending, search_total, settled_or_loading, stale_hashes, terms_document, terms_error,
|
||||
terms_is_loading, try_account, try_accounts, try_cluster_analytics, try_cluster_logs,
|
||||
try_cluster_screenshots, try_default_account, try_game_profile, try_global_analytics,
|
||||
try_leftovers, try_log_content, try_storage_report, use_account, use_accounts,
|
||||
use_add_microsoft_account, use_add_offline_account, use_available_bundles,
|
||||
use_begin_microsoft_login, use_bundle_overrides, use_bundle_updates, use_bundles_with_status,
|
||||
use_cached_image, use_cancel_microsoft_login, use_changelog, use_cluster,
|
||||
use_cluster_analytics, use_cluster_content, use_cluster_logs, use_cluster_mutation,
|
||||
use_cluster_profile, use_cluster_screenshots, use_cluster_settings, use_clusters,
|
||||
use_current_account, use_default_account, use_disable_warnings, use_discard_leftovers,
|
||||
use_finish_microsoft_login, use_game_profile, use_game_versions, use_global_analytics,
|
||||
use_java_majors, use_java_runtimes, use_leftovers,
|
||||
use_loader_versions, use_local_image, use_log_action, use_log_content, use_migration,
|
||||
LeftoversQuery, LogAction, LogContentQuery, MigrationQuery, OnboardingBundlesQuery,
|
||||
RefreshAccountKeys, RemoveAccountKeys, ScreenshotAction, SetDefaultAccountKeys, StorageAction,
|
||||
StorageActionMutation, StorageReportQuery, TermsQuery, UploadLogKeys, UploadLogMutation,
|
||||
UseDiscardLeftovers, UseLogAction, UseRefreshAccount, UseRemoveAccount, UseScreenshotAction,
|
||||
UseSetDefaultAccount, UseStorageAction, UseUploadLog, VERSIONS_PAGE_SIZE,
|
||||
accounts_have_microsoft, available_bundles, bundle_overrides_map, bundles_with_status_items,
|
||||
category_list, changelog_error, changelog_groups, changelog_is_loading, cluster_art_url,
|
||||
cluster_content_items, content_type_for_slug, disable_warnings, game_versions,
|
||||
has_migration_data, invalidate_cluster_content_queries, invalidate_cluster_queries,
|
||||
invalidate_java_queries, invalidate_leftovers_queries, invalidate_logs_queries,
|
||||
invalidate_profile_queries, invalidate_screenshots_queries, invalidate_storage_queries,
|
||||
java_majors, java_runtimes, latest_changelog_version, loaded_image, loader_versions,
|
||||
login_code_already_handled, migration_detection, mutation_error, mutation_is_pending,
|
||||
mutation_is_running, mutation_ok, onboarding_bundles_items, package_meta_batch,
|
||||
package_updates, pick_version_metadata, project_detail, provider_versions, query_error,
|
||||
query_is_busy, query_is_loading, refresh_version_art_gallery, reset_login_code_dedup,
|
||||
resolve_art_url, search_items, search_pending, search_total, settled_or_loading, stale_hashes,
|
||||
terms_document, terms_error, terms_is_loading, try_account, try_accounts,
|
||||
try_cluster_analytics, try_cluster_logs, try_cluster_screenshots, try_default_account,
|
||||
try_game_profile, try_global_analytics, try_leftovers, try_log_content, try_storage_report,
|
||||
use_account, use_accounts, use_add_microsoft_account, use_add_offline_account,
|
||||
use_available_bundles, use_begin_microsoft_login, use_bundle_overrides, use_bundle_updates,
|
||||
use_bundles_with_status, use_cached_image, use_cancel_microsoft_login, use_changelog,
|
||||
use_cluster, use_cluster_analytics, use_cluster_content, use_cluster_logs,
|
||||
use_cluster_mutation, use_cluster_profile, use_cluster_screenshots, use_cluster_settings,
|
||||
use_clusters, use_current_account, use_default_account, use_disable_warnings,
|
||||
use_discard_leftovers, use_finish_microsoft_login, use_game_profile, use_game_versions,
|
||||
use_global_analytics, use_java_majors, use_java_runtimes, use_leftovers, use_loader_versions,
|
||||
use_local_image, use_log_action, use_log_content, use_migratable_routes, use_migration,
|
||||
use_named_profiles, use_onboarding_bundles, use_package_categories, use_package_meta_batch,
|
||||
use_package_project, use_package_search, use_package_updates, use_package_versions,
|
||||
use_package_versions_when, use_player_profile, use_player_skin, use_provider_versions,
|
||||
use_refresh_account, use_refresh_all_accounts, use_remove_account, use_screenshot_action,
|
||||
use_screenshot_folder_watch, use_set_default_account, use_storage_action, use_storage_report,
|
||||
use_terms, use_upload_log, use_version_art, use_version_art_gallery, use_version_loaders,
|
||||
use_version_metadata, use_versions, version_list, versions_metadata,
|
||||
versions_total,
|
||||
use_version_metadata, use_versions, version_list, versions_metadata, versions_total,
|
||||
};
|
||||
pub use queries::{
|
||||
add_world_datapacks, delete_world, delete_world_datapack, invalidate_world_contents,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
use freya::query::{Query, QueryCapability, UseQuery, use_query};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use oneclient_content::packages::release_migration::has_packages_to_migrate;
|
||||
use oneclient_content::packages::{ContentType, PackageStore};
|
||||
use oneclient_core::{LauncherError, LinkedArtifactInfo};
|
||||
use oneclient_db::models::ClusterId;
|
||||
|
||||
use crate::launcher::off_ui;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ClusterContentQuery {
|
||||
pub cluster_id: ClusterId,
|
||||
|
|
@ -50,3 +56,35 @@ pub fn use_cluster_content(
|
|||
pub fn cluster_content_items(query: &UseQuery<ClusterContentQuery>) -> Vec<LinkedArtifactInfo> {
|
||||
super::state::settled_or_loading(query).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct MigratableRoutesQuery;
|
||||
|
||||
impl QueryCapability for MigratableRoutesQuery {
|
||||
type Ok = Arc<HashSet<(ClusterId, ClusterId)>>;
|
||||
type Err = LauncherError;
|
||||
type Keys = Vec<(ClusterId, ClusterId)>;
|
||||
|
||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||
let state = crate::launcher::state()?;
|
||||
let routes = keys.clone();
|
||||
off_ui(async move {
|
||||
let content = state.services.content();
|
||||
let mut migratable = HashSet::new();
|
||||
for (target, source) in routes {
|
||||
if has_packages_to_migrate(source, target, &state.bundles, &content).await? {
|
||||
migratable.insert((target, source));
|
||||
}
|
||||
}
|
||||
Ok(Arc::new(migratable))
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_migratable_routes(
|
||||
routes: Vec<(ClusterId, ClusterId)>,
|
||||
) -> Arc<HashSet<(ClusterId, ClusterId)>> {
|
||||
let query = use_query(Query::new(routes, MigratableRoutesQuery));
|
||||
super::state::settled_or_loading(&query).unwrap_or_default()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ pub use changelog::{
|
|||
changelog_error, changelog_groups, changelog_is_loading, latest_changelog_version,
|
||||
use_changelog,
|
||||
};
|
||||
pub use cluster_content::{cluster_content_items, use_cluster_content};
|
||||
pub use cluster_content::{cluster_content_items, use_cluster_content, use_migratable_routes};
|
||||
pub use clusters::{use_cluster, use_clusters};
|
||||
pub use disable_warnings::{DisableWarningsQuery, disable_warnings, use_disable_warnings};
|
||||
pub(crate) use folder_watch::use_folder_watch;
|
||||
|
|
@ -108,10 +108,8 @@ pub use version_metadata::{
|
|||
use_version_art, use_version_art_gallery, use_version_metadata,
|
||||
};
|
||||
pub use versions::{
|
||||
GameVersion, JavaMajorsQuery, game_versions, java_majors,
|
||||
loader_versions, use_game_versions, use_java_majors,
|
||||
use_loader_versions, use_version_loaders, use_versions,
|
||||
versions_metadata,
|
||||
GameVersion, JavaMajorsQuery, game_versions, java_majors, loader_versions, use_game_versions,
|
||||
use_java_majors, use_loader_versions, use_version_loaders, use_versions, versions_metadata,
|
||||
};
|
||||
pub use worlds::{
|
||||
add_world_datapacks, delete_world, delete_world_datapack, invalidate_world_contents,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::collections::HashSet;
|
|||
use std::path::PathBuf;
|
||||
|
||||
use super::bundles::{BundleOverridesQuery, BundleUpdatesQuery, BundlesWithStatusQuery};
|
||||
use super::cluster_content::ClusterContentQuery;
|
||||
use super::cluster_content::{ClusterContentQuery, MigratableRoutesQuery};
|
||||
use super::clusters::ListClustersQuery;
|
||||
use super::package_updates::PackageUpdatesQuery;
|
||||
use super::settings_profiles::{
|
||||
|
|
@ -42,6 +42,11 @@ pub async fn invalidate_cluster_queries() {
|
|||
QueriesStorage::<BundlesWithStatusQuery>::invalidate_all(),
|
||||
)
|
||||
.await;
|
||||
timed(
|
||||
"migratable_routes",
|
||||
QueriesStorage::<MigratableRoutesQuery>::invalidate_all(),
|
||||
)
|
||||
.await;
|
||||
timed(
|
||||
"clusters",
|
||||
QueriesStorage::<ListClustersQuery>::invalidate_all(),
|
||||
|
|
@ -68,6 +73,7 @@ pub async fn invalidate_cluster_queries() {
|
|||
/// this before dropping its busy flag
|
||||
pub async fn invalidate_cluster_content_queries() {
|
||||
QueriesStorage::<ClusterContentQuery>::invalidate_all().await;
|
||||
QueriesStorage::<MigratableRoutesQuery>::invalidate_all().await;
|
||||
}
|
||||
|
||||
/// Everything [`invalidate_cluster_queries`] does bar the cluster list and the
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ use crate::hooks::{
|
|||
ClusterAction, java_runtimes, loader_versions, mutation_is_running, query_error,
|
||||
settled_or_loading, try_game_profile, use_cluster_mutation, use_clusters, use_dispatch,
|
||||
use_game_profile, use_game_snapshot, use_java_runtimes, use_loader_versions,
|
||||
use_release_migration_checking, use_settings_snapshot,
|
||||
use_migratable_routes, use_release_migration_checking, use_settings_snapshot,
|
||||
};
|
||||
use crate::layout::cluster_content;
|
||||
use crate::theme::colors;
|
||||
use crate::ui::centered_note;
|
||||
use crate::view::app::clusters::{DeleteInstanceModal, EditInstanceModal, InstanceFacts};
|
||||
use crate::view::app::settings::{section_header, settings_row, settings_row_disabled};
|
||||
use oneclient_core::clusters::rank_migration_sources;
|
||||
use oneclient_core::clusters::{can_migrate_manually, rank_migration_sources};
|
||||
|
||||
use super::cluster_not_found;
|
||||
use super::modpack_settings::{ModpackRepairRow, ModpackUpdateRow};
|
||||
|
|
@ -890,29 +890,47 @@ impl Component for MigrateFromRow {
|
|||
let cluster_id = self.cluster_id;
|
||||
let dispatch = use_dispatch();
|
||||
let checking = use_release_migration_checking(cluster_id);
|
||||
let running = use_game_snapshot().is_running(cluster_id);
|
||||
let game = use_game_snapshot();
|
||||
let clusters = settled_or_loading(&use_clusters()).unwrap_or_default();
|
||||
let mut picked = use_state(|| None::<i64>);
|
||||
let mut picked = use_state(|| None::<(i64, i64)>);
|
||||
|
||||
let sources = clusters
|
||||
.iter()
|
||||
.find(|cluster| cluster.id == cluster_id)
|
||||
.map(|target| rank_migration_sources(target, &clusters))
|
||||
.unwrap_or_default();
|
||||
let mut routes: Vec<(String, i64, i64)> = Vec::new();
|
||||
if let Some(this) = clusters.iter().find(|cluster| cluster.id == cluster_id) {
|
||||
routes.extend(
|
||||
rank_migration_sources(this, &clusters)
|
||||
.into_iter()
|
||||
.map(|source| (format!("From {}", source.name), cluster_id, source.id)),
|
||||
);
|
||||
routes.extend(
|
||||
clusters
|
||||
.iter()
|
||||
.filter(|other| {
|
||||
other.id != cluster_id
|
||||
&& can_migrate_manually(this.mc_loader, other.mc_loader)
|
||||
})
|
||||
.map(|target| (format!("To {}", target.name), target.id, cluster_id)),
|
||||
);
|
||||
}
|
||||
let has_partners = !routes.is_empty();
|
||||
let migratable =
|
||||
use_migratable_routes(routes.iter().map(|route| (route.1, route.2)).collect());
|
||||
routes.retain(|route| migratable.contains(&(route.1, route.2)));
|
||||
|
||||
let selected = picked
|
||||
.read()
|
||||
.and_then(|id| sources.iter().find(|source| source.id == id))
|
||||
.or_else(|| sources.first())
|
||||
.and_then(|pair| routes.iter().find(|route| (route.1, route.2) == pair))
|
||||
.or_else(|| routes.first())
|
||||
.cloned();
|
||||
let selected_id = selected.as_ref().map(|source| source.id);
|
||||
let selected_pair = selected.as_ref().map(|route| (route.1, route.2));
|
||||
let running = selected_pair
|
||||
.is_some_and(|(target, source)| game.is_running(target) || game.is_running(source));
|
||||
|
||||
let mut button = Button::new()
|
||||
.secondary()
|
||||
.enabled(selected_id.is_some() && !checking && !running)
|
||||
.enabled(selected_pair.is_some() && !checking && !running)
|
||||
.on_press(move |_| {
|
||||
if let Some(source_id) = selected_id {
|
||||
dispatch.open_manual_migration(cluster_id, source_id);
|
||||
if let Some((target, source)) = selected_pair {
|
||||
dispatch.open_manual_migration(target, source);
|
||||
}
|
||||
});
|
||||
button = if checking {
|
||||
|
|
@ -928,33 +946,34 @@ impl Component for MigrateFromRow {
|
|||
button = button.tooltip("Close the game before migrating");
|
||||
}
|
||||
|
||||
let picker: Element = if sources.is_empty() {
|
||||
let picker: Element = if routes.is_empty() {
|
||||
label()
|
||||
.text("No other clusters with this loader")
|
||||
.text(if has_partners {
|
||||
"Nothing installed from the browser left to copy"
|
||||
} else {
|
||||
"No other clusters with this loader"
|
||||
})
|
||||
.font_size(12.)
|
||||
.color(colors::fg_secondary())
|
||||
.into_element()
|
||||
} else {
|
||||
let ids: Vec<i64> = sources.iter().map(|source| source.id).collect();
|
||||
let names: Vec<String> = sources.iter().map(|source| source.name.clone()).collect();
|
||||
Dropdown::new(
|
||||
selected.map(|source| source.name).unwrap_or_default(),
|
||||
names,
|
||||
)
|
||||
.width(Size::px(180.))
|
||||
.height(Size::px(34.))
|
||||
.on_select(move |index: usize| {
|
||||
if let Some(id) = ids.get(index) {
|
||||
picked.set(Some(*id));
|
||||
}
|
||||
})
|
||||
.into_element()
|
||||
let pairs: Vec<(i64, i64)> = routes.iter().map(|route| (route.1, route.2)).collect();
|
||||
let names: Vec<String> = routes.iter().map(|route| route.0.clone()).collect();
|
||||
Dropdown::new(selected.map(|route| route.0).unwrap_or_default(), names)
|
||||
.width(Size::px(220.))
|
||||
.height(Size::px(34.))
|
||||
.on_select(move |index: usize| {
|
||||
if let Some(pair) = pairs.get(index) {
|
||||
picked.set(Some(*pair));
|
||||
}
|
||||
})
|
||||
.into_element()
|
||||
};
|
||||
|
||||
settings_row(
|
||||
IconType::Copy01,
|
||||
"Migrate",
|
||||
"Bring mods, resource packs and shaders you installed in another cluster. Compatible versions are downloaded; the other cluster is left as it is.",
|
||||
"Copy mods, resource packs and shaders you installed from the browser to or from another cluster.",
|
||||
rect()
|
||||
.horizontal()
|
||||
.cross_align(Alignment::Center)
|
||||
|
|
|
|||
|
|
@ -150,6 +150,23 @@ pub async fn has_migratable_packages(cluster_id: i64, ctx: &ContentCtx) -> Conte
|
|||
Ok(!migratable_candidates(cluster_id, ctx).await?.is_empty())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(bundles, ctx))]
|
||||
pub async fn has_packages_to_migrate(
|
||||
source_cluster_id: i64,
|
||||
target_cluster_id: i64,
|
||||
bundles: &BundlesManager,
|
||||
ctx: &ContentCtx,
|
||||
) -> ContentResult<bool> {
|
||||
let candidates = migratable_candidates(source_cluster_id, ctx).await?;
|
||||
if candidates.is_empty() {
|
||||
return Ok(false);
|
||||
}
|
||||
let present = target_projects(target_cluster_id, bundles, ctx).await?;
|
||||
Ok(candidates
|
||||
.iter()
|
||||
.any(|candidate| !present.contains(&candidate.project_id)))
|
||||
}
|
||||
|
||||
async fn target_projects(
|
||||
target_cluster_id: i64,
|
||||
bundles: &BundlesManager,
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ pub use prepare::{
|
|||
};
|
||||
pub use provision::{ensure_from_bundles, ensure_from_versions};
|
||||
pub use release_migration::{
|
||||
OfferLookup, ReleaseMigrationOffer, manual_migration_offer, rank_migration_sources,
|
||||
record_new_versions, release_migration_offer,
|
||||
OfferLookup, ReleaseMigrationOffer, can_migrate_manually, manual_migration_offer,
|
||||
rank_migration_sources, record_new_versions, release_migration_offer,
|
||||
};
|
||||
pub use unlink_legacy::{SweepReport, unlink_legacy_cluster_content};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ use oneclient_common::domain::GameLoader;
|
|||
|
||||
use oneclient_cluster::Cluster;
|
||||
use oneclient_common::version::parse_mc_version;
|
||||
use oneclient_content::packages::release_migration::has_migratable_packages;
|
||||
use oneclient_content::packages::release_migration::{
|
||||
has_migratable_packages, has_packages_to_migrate,
|
||||
};
|
||||
|
||||
use crate::LauncherResult;
|
||||
use crate::state::LauncherState;
|
||||
|
|
@ -50,9 +52,7 @@ fn is_migration_source(
|
|||
target: &Cluster,
|
||||
loader_ok: impl Fn(GameLoader, GameLoader) -> bool,
|
||||
) -> bool {
|
||||
candidate.id != target.id
|
||||
&& !candidate.user_created
|
||||
&& loader_ok(candidate.mc_loader, target.mc_loader)
|
||||
candidate.id != target.id && loader_ok(candidate.mc_loader, target.mc_loader)
|
||||
}
|
||||
|
||||
fn is_migration_destination(target: &ReleaseTarget, rules: &[RemoteMigration]) -> bool {
|
||||
|
|
@ -142,7 +142,7 @@ pub async fn manual_migration_offer(
|
|||
};
|
||||
|
||||
if !is_migration_source(source, target, can_migrate_manually)
|
||||
|| !has_migratable_packages(source.id, &content).await?
|
||||
|| !has_packages_to_migrate(source.id, target.id, &state.bundles, &content).await?
|
||||
{
|
||||
return Ok(OfferLookup::NoSources);
|
||||
}
|
||||
|
|
@ -188,7 +188,7 @@ async fn offer_for(
|
|||
|
||||
let mut sources = Vec::new();
|
||||
for cluster in clusters {
|
||||
if !is_migration_source(cluster, target, |from, to| from == to) {
|
||||
if cluster.user_created || !is_migration_source(cluster, target, |from, to| from == to) {
|
||||
continue;
|
||||
}
|
||||
if !version_order(&cluster.mc_version).is_some_and(|order| order != target_order) {
|
||||
|
|
@ -335,4 +335,34 @@ mod tests {
|
|||
fn a_bare_minor_orders_below_its_patches() {
|
||||
assert!(version_order("26.3") < version_order("26.3.1"));
|
||||
}
|
||||
|
||||
fn user_cluster(id: i64, mc_version: &str) -> Cluster {
|
||||
Cluster {
|
||||
id,
|
||||
name: format!("{mc_version} Fabric"),
|
||||
folder_name: format!("cluster-{id}"),
|
||||
setting_profile_name: None,
|
||||
mc_version: mc_version.to_string(),
|
||||
mc_loader: GameLoader::Fabric,
|
||||
mc_loader_version: None,
|
||||
stage: oneclient_cluster::ClusterStage::default(),
|
||||
created_at: None,
|
||||
last_played: None,
|
||||
overall_played: std::time::Duration::ZERO,
|
||||
linked_modpack_hash: None,
|
||||
kind: oneclient_cluster::ClusterKind::OneClient,
|
||||
user_created: true,
|
||||
description: None,
|
||||
tags: Vec::new(),
|
||||
cover_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_migration_lists_user_created_sources() {
|
||||
let target = user_cluster(1, "26.2");
|
||||
let clusters = [target.clone(), user_cluster(2, "26.3")];
|
||||
let sources = rank_migration_sources(&target, &clusters);
|
||||
assert_eq!(sources.iter().map(|c| c.id).collect::<Vec<_>>(), [2]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue