use std::path::Path;
use anyhow::Result;
use crate::{
analytics::{AnalyticsEventName, send_analytics_deferred},
config::Config,
db::{LocalDatabase, RemoteDatabase},
download::install_mods_parallel,
file::deserialize_from_json,
toggle::toggle_mod,
};
pub fn export_mods(db: &LocalDatabase) -> Result<String> {
let enabled_mods: Vec<&String> = db.active().map(|m| &m.manifest.unique_name).collect();
let result = serde_json::to_string_pretty(&enabled_mods)?;
Ok(result)
}
pub async fn import_mods(
config: &Config,
local_db: &LocalDatabase,
remote_db: &RemoteDatabase,
file_path: &Path,
disable_missing: bool,
) -> Result<()> {
let unique_names: Vec<String> = deserialize_from_json(file_path)?;
let mut needed_install: Vec<String> = vec![];
if disable_missing {
for local_mod in local_db.valid() {
if local_mod.enabled {
toggle_mod(&local_mod.manifest.unique_name, local_db, false, false)?;
}
}
}
for name in unique_names.iter() {
let local_mod = local_db.get_mod(name);
if let Some(local_mod) = local_mod {
toggle_mod(&local_mod.manifest.unique_name, local_db, true, false)?;
} else {
needed_install.push(name.to_string());
}
}
install_mods_parallel(needed_install.clone(), config, remote_db, local_db).await?;
for unique_name in needed_install {
send_analytics_deferred(AnalyticsEventName::ModInstall, &unique_name, config).await;
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::{fs, path::PathBuf};
use crate::test_utils::{TestContext, get_test_file};
use super::*;
fn make_list_json(ctx: &TestContext, list: &str) -> PathBuf {
let path = ctx.temp_dir.path().join("list.json");
fs::write(&path, list).unwrap();
path
}
#[test]
fn test_export_mods() {
let test_dir = get_test_file("");
let db = LocalDatabase::fetch(test_dir.to_str().unwrap()).unwrap();
let result = export_mods(&db).unwrap();
assert!(result.contains("Bwc9876.TimeSaver"));
assert!(!result.contains("Bwc9876.SaveEditor"));
}
#[test]
fn test_import_mods() {
tokio_test::block_on(async {
let mut ctx = TestContext::new();
ctx.fetch_remote_db().await;
let list_path = make_list_json(&ctx, "[\"Bwc9876.TimeSaver\"]");
import_mods(
&ctx.config,
&ctx.local_db,
&ctx.remote_db,
&list_path,
false,
)
.await
.unwrap();
assert!(ctx.get_test_path("Bwc9876.TimeSaver").is_dir());
});
}
#[test]
fn test_import_mods_with_disabled() {
tokio_test::block_on(async {
let mut ctx = TestContext::new();
ctx.fetch_remote_db().await;
ctx.install_test_zip("Bwc9876.TimeSaver.zip", true);
toggle_mod("Bwc9876.TimeSaver", &ctx.local_db, false, false).unwrap();
let list_path = make_list_json(&ctx, "[\"Bwc9876.TimeSaver\"]");
import_mods(
&ctx.config,
&ctx.local_db,
&ctx.remote_db,
&list_path,
false,
)
.await
.unwrap();
ctx.fetch_local_db();
let new_mod = ctx.local_db.get_mod("Bwc9876.TimeSaver").unwrap();
assert!(new_mod.enabled);
});
}
#[test]
fn test_import_mods_disable_missing() {
tokio_test::block_on(async {
let mut ctx = TestContext::new();
ctx.fetch_remote_db().await;
ctx.install_test_zip("Bwc9876.TimeSaver.zip", true);
let list_path = make_list_json(&ctx, "[]");
import_mods(&ctx.config, &ctx.local_db, &ctx.remote_db, &list_path, true)
.await
.unwrap();
ctx.fetch_local_db();
let new_mod = ctx.local_db.get_mod("Bwc9876.TimeSaver").unwrap();
assert!(!new_mod.enabled);
});
}
#[test]
fn test_import_mods_disable_missing_already_enabled() {
tokio_test::block_on(async {
let mut ctx = TestContext::new();
ctx.fetch_remote_db().await;
ctx.install_test_zip("Bwc9876.TimeSaver.zip", true);
let list_path = make_list_json(&ctx, "[\"Bwc9876.TimeSaver\"]");
import_mods(&ctx.config, &ctx.local_db, &ctx.remote_db, &list_path, true)
.await
.unwrap();
ctx.fetch_local_db();
let new_mod = ctx.local_db.get_mod("Bwc9876.TimeSaver").unwrap();
assert!(new_mod.enabled);
});
}
}