aboutsummaryrefslogtreecommitdiff
path: root/src-tauri/src/northstar
diff options
context:
space:
mode:
Diffstat (limited to 'src-tauri/src/northstar')
-rw-r--r--src-tauri/src/northstar/install.rs358
-rw-r--r--src-tauri/src/northstar/mod.rs276
-rw-r--r--src-tauri/src/northstar/profile.rs121
3 files changed, 0 insertions, 755 deletions
diff --git a/src-tauri/src/northstar/install.rs b/src-tauri/src/northstar/install.rs
deleted file mode 100644
index 0953fa38..00000000
--- a/src-tauri/src/northstar/install.rs
+++ /dev/null
@@ -1,358 +0,0 @@
-use anyhow::Result;
-use serde::{Deserialize, Serialize};
-use std::time::Duration;
-use std::{cell::RefCell, time::Instant};
-use ts_rs::TS;
-
-use crate::constants::{CORE_MODS, NORTHSTAR_DEFAULT_PROFILE, NORTHSTAR_DLL};
-use crate::{
- util::{extract, move_dir_all},
- GameInstall, InstallType,
-};
-
-#[cfg(target_os = "windows")]
-use crate::platform_specific::windows;
-
-#[derive(Serialize, Deserialize, Debug, Clone, TS)]
-#[ts(export)]
-enum InstallState {
- Downloading,
- Extracting,
- Done,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone, TS)]
-#[ts(export)]
-struct InstallProgress {
- current_downloaded: u64,
- total_size: u64,
- state: InstallState,
-}
-
-/// Installs Northstar to the given path
-#[tauri::command]
-pub async fn install_northstar_wrapper(
- window: tauri::Window,
- game_install: GameInstall,
- northstar_package_name: Option<String>,
- version_number: Option<String>,
-) -> Result<bool, String> {
- log::info!("Running Northstar install");
-
- // Get Northstar package name (`Northstar` vs `NorthstarReleaseCandidate`)
- let northstar_package_name = northstar_package_name
- .map(|name| {
- if name.len() <= 1 {
- "Northstar".to_string()
- } else {
- name
- }
- })
- .unwrap_or("Northstar".to_string());
-
- match install_northstar(window, game_install, northstar_package_name, version_number).await {
- Ok(_) => Ok(true),
- Err(err) => {
- log::error!("{}", err);
- Err(err)
- }
- }
-}
-
-/// Update Northstar install in the given path
-#[tauri::command]
-pub async fn update_northstar(
- window: tauri::Window,
- game_install: GameInstall,
- northstar_package_name: Option<String>,
-) -> Result<bool, String> {
- log::info!("Updating Northstar");
-
- // Simply re-run install with up-to-date version for upate
- install_northstar_wrapper(window, game_install, northstar_package_name, None).await
-}
-
-/// Copied from `papa` source code and modified
-///Install N* from the provided mod
-///
-///Checks cache, else downloads the latest version
-async fn do_install(
- window: tauri::Window,
- nmod: &thermite::model::ModVersion,
- game_install: GameInstall,
-) -> Result<()> {
- let filename = format!("northstar-{}.zip", nmod.version);
- let temp_dir = format!("{}/___flightcore-temp", game_install.game_path);
- let download_directory = format!("{}/download-dir", temp_dir);
- let extract_directory = format!("{}/extract-dir", temp_dir);
-
- log::info!("Attempting to create temporary directory {}", temp_dir);
- std::fs::create_dir_all(download_directory.clone())?;
- std::fs::create_dir_all(extract_directory.clone())?;
-
- let download_path = format!("{}/{}", download_directory, filename);
- log::info!("Download path: {download_path}");
-
- let last_emit = RefCell::new(Instant::now()); // Keep track of the last time a signal was emitted
- let mut nfile = std::fs::File::options()
- .read(true)
- .write(true)
- .truncate(true)
- .create(true)
- .open(download_path)?;
- thermite::core::manage::download_with_progress(
- &mut nfile,
- &nmod.url,
- |delta, current, total| {
- if delta != 0 {
- // Only emit a signal once every 100ms
- // This way we don't bombard the frontend with events on fast download speeds
- let time_since_last_emit = Instant::now().duration_since(*last_emit.borrow());
- if time_since_last_emit >= Duration::from_millis(100) {
- window
- .emit(
- "northstar-install-download-progress",
- InstallProgress {
- current_downloaded: current,
- total_size: total,
- state: InstallState::Downloading,
- },
- )
- .unwrap();
- *last_emit.borrow_mut() = Instant::now();
- }
- }
- },
- )?;
-
- window
- .emit(
- "northstar-install-download-progress",
- InstallProgress {
- current_downloaded: 0,
- total_size: 0,
- state: InstallState::Extracting,
- },
- )
- .unwrap();
-
- log::info!("Extracting Northstar...");
- extract(nfile, std::path::Path::new(&extract_directory))?;
-
- // Prepare Northstar for Installation
- log::info!("Preparing Northstar...");
- if game_install.profile != NORTHSTAR_DEFAULT_PROFILE {
- // We are using a non standard Profile, we must:
- // - move the DLL
- // - rename the Profile
-
- // Move DLL into the default R2Northstar Profile
- let old_dll_path = format!("{}/{}", extract_directory, NORTHSTAR_DLL);
- let new_dll_path = format!(
- "{}/{}/{}",
- extract_directory, NORTHSTAR_DEFAULT_PROFILE, NORTHSTAR_DLL
- );
- std::fs::rename(old_dll_path, new_dll_path)?;
-
- // rename default R2Northstar Profile to the profile we want to use
- let old_profile_path = format!("{}/{}/", extract_directory, NORTHSTAR_DEFAULT_PROFILE);
- let new_profile_path = format!("{}/{}/", extract_directory, game_install.profile);
- std::fs::rename(old_profile_path, new_profile_path)?;
- }
-
- log::info!("Installing Northstar...");
-
- // Delete previous version here
- for core_mod in CORE_MODS {
- let path_to_delete_string = format!(
- "{}/{}/mods/{}/",
- game_install.game_path, game_install.profile, core_mod
- );
- log::info!("Preparing to remove {}", path_to_delete_string);
-
- // Check if folder exists
- let path_to_delete = std::path::Path::new(&path_to_delete_string);
-
- // Check if path even exists before we attempt to remove
- if !path_to_delete.exists() {
- log::info!("{} does not exist. Skipping", path_to_delete_string);
- continue;
- }
-
- if !path_to_delete.is_dir() {
- log::error!(
- "{} exists but is a file? This should never happen",
- path_to_delete_string
- );
- continue;
- }
-
- // Safety check for mod.json
- // Just so that we won't ever have a https://github.com/ValveSoftware/steam-for-linux/issues/3671 moment
- let mod_json_path = format!("{}/mod.json", path_to_delete_string);
- let mod_json_path = std::path::Path::new(&mod_json_path);
-
- if !mod_json_path.exists() {
- log::error!("Missing mod.json for {path_to_delete_string} this shouldn't happen");
- continue;
- }
-
- // Finally delete file
- match std::fs::remove_dir_all(path_to_delete) {
- Ok(()) => {
- log::info!("Succesfully removed")
- }
- Err(err) => {
- log::error!("Failed removing {} due to {}", path_to_delete_string, err)
- }
- };
- }
-
- for entry in std::fs::read_dir(extract_directory).unwrap() {
- let entry = entry.unwrap();
- let destination = format!(
- "{}/{}",
- game_install.game_path,
- entry.path().file_name().unwrap().to_str().unwrap()
- );
-
- log::info!("Installing {}", entry.path().display());
- if !entry.file_type().unwrap().is_dir() {
- std::fs::rename(entry.path(), destination)?;
- } else {
- move_dir_all(entry.path(), destination)?;
- }
- }
-
- // Delete old copy
- log::info!("Delete temporary directory");
- std::fs::remove_dir_all(temp_dir).unwrap();
-
- log::info!("Done installing Northstar!");
- window
- .emit(
- "northstar-install-download-progress",
- InstallProgress {
- current_downloaded: 0,
- total_size: 0,
- state: InstallState::Done,
- },
- )
- .unwrap();
-
- Ok(())
-}
-
-pub async fn install_northstar(
- window: tauri::Window,
- game_install: GameInstall,
- northstar_package_name: String,
- version_number: Option<String>,
-) -> Result<String, String> {
- let index = match thermite::api::get_package_index() {
- Ok(res) => res.to_vec(),
- Err(err) => {
- log::warn!("Failed fetching package index due to: {err}");
- return Err("Failed to connect to Thunderstore.".to_string());
- }
- };
- let nmod = index
- .iter()
- .find(|f| f.name.to_lowercase() == northstar_package_name.to_lowercase())
- .ok_or_else(|| panic!("Couldn't find Northstar on thunderstore???"))
- .unwrap();
-
- // Use passed version or latest if no version was passed
- let version = version_number.as_ref().unwrap_or(&nmod.latest);
-
- let game_path = game_install.game_path.clone();
- log::info!("Install path \"{}\"", game_path);
-
- match do_install(window, nmod.versions.get(version).unwrap(), game_install).await {
- Ok(_) => (),
- Err(err) => {
- if game_path
- .to_lowercase()
- .contains(&r"C:\Program Files\".to_lowercase())
- // default is `C:\Program Files\EA Games\Titanfall2`
- {
- return Err(
- "Cannot install to default EA App install path, please move Titanfall2 to a different install location.".to_string(),
- );
- } else {
- return Err(err.to_string());
- }
- }
- }
-
- Ok(nmod.latest.clone())
-}
-
-/// Attempts to find the game install location
-#[tauri::command]
-pub fn find_game_install_location() -> Result<GameInstall, String> {
- // Attempt parsing Steam library directly
- match steamlocate::SteamDir::locate() {
- Ok(steamdir) => {
- #[cfg(target_os = "linux")]
- {
- let snap_dir = match std::env::var("SNAP_USER_DATA") {
- Ok(snap_dir) => std::path::PathBuf::from(snap_dir),
- Err(_) => match dirs::home_dir() {
- Some(path) => path,
- None => std::path::PathBuf::new(),
- }
- .join("snap"),
- };
-
- if steamdir.path().starts_with(snap_dir) {
- log::warn!("Found Steam installed via Snap, you may encounter issues");
- }
- }
-
- match steamdir.find_app(thermite::TITANFALL2_STEAM_ID) {
- Ok(Some((app, library))) => {
- let app_path = library
- .path()
- .join("steamapps")
- .join("common")
- .join(app.install_dir)
- .into_os_string()
- .into_string()
- .unwrap();
-
- let game_install = GameInstall {
- game_path: app_path,
- profile: "R2Northstar".to_string(),
- install_type: InstallType::STEAM,
- };
- return Ok(game_install);
- }
- Ok(None) => log::info!("Couldn't locate your Titanfall 2 Steam install."),
- Err(err) => log::info!(
- "Something went wrong while trying to find Titanfall 2 {}",
- err
- ),
- }
- }
- Err(err) => log::info!("Couldn't locate Steam on this computer! {}", err),
- }
-
- // (On Windows only) try parsing Windows registry for Origin install path
- #[cfg(target_os = "windows")]
- match windows::origin_install_location_detection() {
- Ok(game_path) => {
- let game_install = GameInstall {
- game_path,
- profile: "R2Northstar".to_string(),
- install_type: InstallType::ORIGIN,
- };
- return Ok(game_install);
- }
- Err(err) => {
- log::info!("{}", err);
- }
- };
-
- Err("Could not auto-detect game install location! Please enter it manually.".to_string())
-}
diff --git a/src-tauri/src/northstar/mod.rs b/src-tauri/src/northstar/mod.rs
deleted file mode 100644
index 9953d742..00000000
--- a/src-tauri/src/northstar/mod.rs
+++ /dev/null
@@ -1,276 +0,0 @@
-//! This module deals with handling things around Northstar such as
-//! - getting version number
-pub mod install;
-pub mod profile;
-
-use crate::util::check_ea_app_or_origin_running;
-use crate::{constants::CORE_MODS, platform_specific::get_host_os, GameInstall, InstallType};
-use crate::{NorthstarThunderstoreRelease, NorthstarThunderstoreReleaseWrapper};
-use anyhow::anyhow;
-use serde::{Deserialize, Serialize};
-use ts_rs::TS;
-
-#[derive(Serialize, Deserialize, Debug, Clone, TS)]
-#[ts(export)]
-pub struct NorthstarLaunchOptions {
- launch_via_steam: bool,
- bypass_checks: bool,
-}
-
-/// Gets list of available Northstar versions from Thunderstore
-#[tauri::command]
-pub async fn get_available_northstar_versions(
-) -> Result<Vec<NorthstarThunderstoreReleaseWrapper>, ()> {
- let northstar_package_name = "Northstar";
- let index = thermite::api::get_package_index().unwrap().to_vec();
- let nsmod = index
- .iter()
- .find(|f| f.name.to_lowercase() == northstar_package_name.to_lowercase())
- .ok_or_else(|| panic!("Couldn't find Northstar on thunderstore???"))
- .unwrap();
-
- let mut releases: Vec<NorthstarThunderstoreReleaseWrapper> = vec![];
- for (_version_string, nsmod_version_obj) in nsmod.versions.iter() {
- let current_elem = NorthstarThunderstoreRelease {
- package: nsmod_version_obj.name.clone(),
- version: nsmod_version_obj.version.clone(),
- };
- let current_elem_wrapped = NorthstarThunderstoreReleaseWrapper {
- label: format!(
- "{} v{}",
- nsmod_version_obj.name.clone(),
- nsmod_version_obj.version.clone()
- ),
- value: current_elem,
- };
-
- releases.push(current_elem_wrapped);
- }
-
- releases.sort_by(|a, b| {
- // Parse version number
- let a_ver = semver::Version::parse(&a.value.version).unwrap();
- let b_ver = semver::Version::parse(&b.value.version).unwrap();
- b_ver.partial_cmp(&a_ver).unwrap() // Sort newest first
- });
-
- Ok(releases)
-}
-
-/// Checks if installed Northstar version is up-to-date
-/// false -> Northstar install is up-to-date
-/// true -> Northstar install is outdated
-#[tauri::command]
-pub async fn check_is_northstar_outdated(
- game_install: GameInstall,
- northstar_package_name: Option<String>,
-) -> Result<bool, String> {
- let northstar_package_name = match northstar_package_name {
- Some(northstar_package_name) => {
- if northstar_package_name.len() <= 1 {
- "Northstar".to_string()
- } else {
- northstar_package_name
- }
- }
- None => "Northstar".to_string(),
- };
-
- let index = match thermite::api::get_package_index() {
- Ok(res) => res.to_vec(),
- Err(err) => return Err(format!("Couldn't check if Northstar up-to-date: {err}")),
- };
- let nmod = index
- .iter()
- .find(|f| f.name.to_lowercase() == northstar_package_name.to_lowercase())
- .expect("Couldn't find Northstar on thunderstore???");
- // .ok_or_else(|| anyhow!("Couldn't find Northstar on thunderstore???"))?;
-
- let version_number = match get_northstar_version_number(game_install) {
- Ok(version_number) => version_number,
- Err(err) => {
- log::warn!("{}", err);
- // If we fail to get new version just assume we are up-to-date
- return Err(err);
- }
- };
-
- // Release candidate version numbers are different between `mods.json` and Thunderstore
- let version_number = crate::util::convert_release_candidate_number(version_number);
-
- if version_number != nmod.latest {
- log::info!("Installed Northstar version outdated");
- Ok(true)
- } else {
- log::info!("Installed Northstar version up-to-date");
- Ok(false)
- }
-}
-
-/// Check version number of a mod
-pub fn check_mod_version_number(path_to_mod_folder: &str) -> Result<String, anyhow::Error> {
- let data = std::fs::read_to_string(format!("{path_to_mod_folder}/mod.json"))?;
- let parsed_json: serde_json::Value = serde_json::from_str(&data)?;
-
- let mod_version_number = match parsed_json.get("Version").and_then(|value| value.as_str()) {
- Some(version_number) => version_number,
- None => return Err(anyhow!("No version number found")),
- };
-
- log::info!("{}", mod_version_number);
-
- Ok(mod_version_number.to_string())
-}
-
-/// Returns the current Northstar version number as a string
-#[tauri::command]
-pub fn get_northstar_version_number(game_install: GameInstall) -> Result<String, String> {
- log::info!("{}", game_install.game_path);
-
- // TODO:
- // Check if NorthstarLauncher.exe exists and check its version number
- let initial_version_number = match check_mod_version_number(&format!(
- "{}/{}/mods/{}",
- game_install.game_path, game_install.profile, CORE_MODS[0]
- )) {
- Ok(version_number) => version_number,
- Err(err) => return Err(err.to_string()),
- };
-
- for core_mod in CORE_MODS {
- let current_version_number = match check_mod_version_number(&format!(
- "{}/{}/mods/{}",
- game_install.game_path, game_install.profile, core_mod
- )) {
- Ok(version_number) => version_number,
- Err(err) => return Err(err.to_string()),
- };
- if current_version_number != initial_version_number {
- // We have a version number mismatch
- return Err("Found version number mismatch".to_string());
- }
- }
- log::info!("All mods same version");
-
- Ok(initial_version_number)
-}
-
-/// Launches Northstar
-#[tauri::command]
-pub fn launch_northstar(
- game_install: GameInstall,
- launch_options: NorthstarLaunchOptions,
-) -> Result<String, String> {
- dbg!(game_install.clone());
-
- if launch_options.launch_via_steam {
- return launch_northstar_steam(game_install);
- }
-
- let host_os = get_host_os();
-
- // Explicitly fail early certain (currently) unsupported install setups
- if host_os != "windows" {
- if !matches!(game_install.install_type, InstallType::STEAM) {
- return Err(format!(
- "Not yet implemented for \"{}\" with Titanfall2 installed via \"{:?}\"",
- get_host_os(),
- game_install.install_type
- ));
- }
-
- return launch_northstar_steam(game_install);
- }
-
- // Only check guards if bypassing checks is not enabled
- if !launch_options.bypass_checks {
- // Some safety checks before, should have more in the future
- if get_northstar_version_number(game_install.clone()).is_err() {
- return Err(anyhow!("Not all checks were met").to_string());
- }
-
- // Require EA App or Origin to be running to launch Northstar
- let ea_app_is_running = check_ea_app_or_origin_running();
- if !ea_app_is_running {
- return Err(
- anyhow!("EA App not running, start EA App before launching Northstar").to_string(),
- );
- }
- }
-
- // Switch to Titanfall2 directory for launching
- // NorthstarLauncher.exe expects to be run from that folder
- if std::env::set_current_dir(game_install.game_path.clone()).is_err() {
- // We failed to get to Titanfall2 directory
- return Err(anyhow!("Couldn't access Titanfall2 directory").to_string());
- }
-
- // Only Windows with Steam or Origin are supported at the moment
- if host_os == "windows"
- && (matches!(game_install.install_type, InstallType::STEAM)
- || matches!(game_install.install_type, InstallType::ORIGIN)
- || matches!(game_install.install_type, InstallType::UNKNOWN))
- {
- let ns_exe_path = format!("{}/NorthstarLauncher.exe", game_install.game_path);
- let ns_profile_arg = format!("-profile={}", game_install.profile);
-
- let mut output = std::process::Command::new("C:\\Windows\\System32\\cmd.exe")
- .args(["/C", "start", "", &ns_exe_path, &ns_profile_arg])
- .spawn()
- .expect("failed to execute process");
- output.wait().expect("failed waiting on child process");
- return Ok("Launched game".to_string());
- }
-
- Err(format!(
- "Not yet implemented for {:?} on {}",
- game_install.install_type,
- get_host_os()
- ))
-}
-
-/// Prepare Northstar and Launch through Steam using the Browser Protocol
-pub fn launch_northstar_steam(game_install: GameInstall) -> Result<String, String> {
- if !matches!(game_install.install_type, InstallType::STEAM) {
- return Err("Titanfall2 was not installed via Steam".to_string());
- }
-
- match steamlocate::SteamDir::locate() {
- Ok(steamdir) => {
- if get_host_os() != "windows" {
- match steamdir.compat_tool_mapping() {
- Ok(map) => match map.get(&thermite::TITANFALL2_STEAM_ID) {
- Some(_) => {}
- None => {
- return Err(
- "Titanfall2 was not configured to use a compatibility tool"
- .to_string(),
- );
- }
- },
- Err(_) => {
- return Err("Could not get compatibility tool mapping".to_string());
- }
- }
- }
- }
- Err(_) => {
- return Err("Couldn't access Titanfall2 directory".to_string());
- }
- }
-
- // Switch to Titanfall2 directory to set everything up
- if std::env::set_current_dir(game_install.game_path).is_err() {
- // We failed to get to Titanfall2 directory
- return Err("Couldn't access Titanfall2 directory".to_string());
- }
-
- match open::that(format!(
- "steam://run/{}//-profile={} --northstar/",
- thermite::TITANFALL2_STEAM_ID,
- game_install.profile
- )) {
- Ok(()) => Ok("Started game".to_string()),
- Err(_err) => Err("Failed to launch Titanfall 2 via Steam".to_string()),
- }
-}
diff --git a/src-tauri/src/northstar/profile.rs b/src-tauri/src/northstar/profile.rs
deleted file mode 100644
index 26a32d6b..00000000
--- a/src-tauri/src/northstar/profile.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-use crate::util::copy_dir_all;
-use crate::GameInstall;
-
-// These folders are part of Titanfall 2 and
-// should NEVER be used as a Profile
-const SKIP_PATHS: [&str; 8] = [
- "___flightcore-temp",
- "__overlay",
- "bin",
- "Core",
- "r2",
- "vpk",
- "platform",
- "Support",
-];
-
-// A profile may have one of these to be detected
-const MAY_CONTAIN: [&str; 10] = [
- "mods/",
- "plugins/",
- "packages/",
- "logs/",
- "runtime/",
- "save_data/",
- "Northstar.dll",
- "enabledmods.json",
- "placeholder.playerdata.pdata",
- "LEGAL.txt",
-];
-
-/// Returns a list of Profile names
-/// All the returned Profiles can be found relative to the game path
-#[tauri::command]
-pub fn fetch_profiles(game_install: GameInstall) -> Result<Vec<String>, String> {
- let mut profiles: Vec<String> = Vec::new();
-
- for content in MAY_CONTAIN {
- let pattern = format!("{}/*/{}", game_install.game_path, content);
- for e in glob::glob(&pattern).expect("Failed to read glob pattern") {
- let path = e.unwrap();
- let mut ancestors = path.ancestors();
-
- ancestors.next();
-
- let profile_path = std::path::Path::new(ancestors.next().unwrap());
- let profile_name = profile_path
- .file_name()
- .unwrap()
- .to_os_string()
- .into_string()
- .unwrap();
-
- if !profiles.contains(&profile_name) {
- profiles.push(profile_name);
- }
- }
- }
-
- Ok(profiles)
-}
-
-/// Validates if a given profile is actually a valid profile
-#[tauri::command]
-pub fn validate_profile(game_install: GameInstall, profile: String) -> bool {
- // Game files are never a valid profile
- // Prevent users with messed up installs from making it even worse
- if SKIP_PATHS.contains(&profile.as_str()) {
- return false;
- }
-
- log::info!("Validating Profile {}", profile);
-
- let profile_path = format!("{}/{}", game_install.game_path, profile);
- let profile_dir = std::path::Path::new(profile_path.as_str());
-
- profile_dir.is_dir()
-}
-
-#[tauri::command]
-pub fn delete_profile(game_install: GameInstall, profile: String) -> Result<(), String> {
- // Check if the Profile actually exists
- if !validate_profile(game_install.clone(), profile.clone()) {
- return Err(format!("{} is not a valid Profile", profile));
- }
-
- log::info!("Deleting Profile {}", profile);
-
- let profile_path = format!("{}/{}", game_install.game_path, profile);
-
- match std::fs::remove_dir_all(profile_path) {
- Ok(()) => Ok(()),
- Err(err) => Err(format!("Failed to delete Profile: {}", err)),
- }
-}
-
-/// Clones a profile by simply duplicating the folder under a new name
-#[tauri::command]
-pub fn clone_profile(
- game_install: GameInstall,
- old_profile: String,
- new_profile: String,
-) -> Result<(), String> {
- // Check if the old Profile already exists
- if !validate_profile(game_install.clone(), old_profile.clone()) {
- return Err(format!("{} is not a valid Profile", old_profile));
- }
-
- // Check that new Profile does not already exist
- if validate_profile(game_install.clone(), new_profile.clone()) {
- return Err(format!("{} already exists", new_profile));
- }
-
- log::info!("Cloning Profile {} to {}", old_profile, new_profile);
-
- let old_profile_path = format!("{}/{}", game_install.game_path, old_profile);
- let new_profile_path = format!("{}/{}", game_install.game_path, new_profile);
-
- copy_dir_all(old_profile_path, new_profile_path).unwrap();
-
- Ok(())
-}