2024-08-16 00:31:02 +01:00
|
|
|
mod mod_meta;
|
2024-08-17 16:11:04 +01:00
|
|
|
mod modpack;
|
2024-08-20 20:00:43 +01:00
|
|
|
mod profiles;
|
2024-08-17 16:11:04 +01:00
|
|
|
mod providers;
|
2024-08-16 00:31:02 +01:00
|
|
|
mod resolver;
|
2024-08-13 23:56:57 +01:00
|
|
|
|
2024-08-20 20:00:43 +01:00
|
|
|
use clap::{Args, Parser, Subcommand};
|
2024-08-16 00:31:02 +01:00
|
|
|
use mod_meta::{ModMeta, ModProvider};
|
|
|
|
use modpack::ModpackMeta;
|
2024-08-20 20:00:43 +01:00
|
|
|
use profiles::{PackSource, Profile};
|
2024-08-18 23:32:23 +01:00
|
|
|
use providers::DownloadSide;
|
2024-08-17 21:17:59 +01:00
|
|
|
use std::{error::Error, path::PathBuf};
|
2024-08-13 23:56:57 +01:00
|
|
|
|
|
|
|
/// A Minecraft Modpack Manager
|
|
|
|
#[derive(Parser)]
|
2024-08-20 20:00:43 +01:00
|
|
|
#[command(author, version, about, long_about = None)]
|
2024-08-13 23:56:57 +01:00
|
|
|
struct Cli {
|
|
|
|
#[command(subcommand)]
|
|
|
|
command: Option<Commands>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Subcommand)]
|
|
|
|
enum Commands {
|
2024-08-14 22:32:45 +01:00
|
|
|
/// Initialise a new mcmpmgr project in the specified directory (or current dir if not specified)
|
2024-08-13 23:56:57 +01:00
|
|
|
Init {
|
2024-08-14 21:28:40 +01:00
|
|
|
/// The root modpack project directory
|
2024-08-13 23:56:57 +01:00
|
|
|
directory: Option<PathBuf>,
|
2024-08-14 21:28:40 +01:00
|
|
|
/// Name of the modpack project
|
|
|
|
#[arg(long)]
|
|
|
|
name: Option<String>,
|
|
|
|
/// The modpack's Minecraft version
|
2024-08-13 23:56:57 +01:00
|
|
|
#[arg(long, default_value_t = String::from("1.20.1"))]
|
|
|
|
mc_version: String,
|
2024-08-14 21:28:40 +01:00
|
|
|
/// The modpack's modloader
|
|
|
|
#[arg(long, default_value_t = modpack::ModLoader::Fabric)]
|
|
|
|
modloader: modpack::ModLoader,
|
2024-08-14 22:45:09 +01:00
|
|
|
/// Default providers to download the mods from for the modpack (can be overridden on a per-mod basis)
|
|
|
|
#[arg(long)]
|
|
|
|
providers: Vec<ModProvider>,
|
2024-08-14 21:28:40 +01:00
|
|
|
},
|
2024-08-14 22:32:45 +01:00
|
|
|
/// Create and initialise a new mcmpmgr project in the current directory
|
2024-08-14 21:28:40 +01:00
|
|
|
New {
|
|
|
|
/// Name of the new modpack project
|
|
|
|
name: String,
|
|
|
|
/// The modpack's Minecraft version
|
|
|
|
#[arg(long, default_value_t = String::from("1.20.1"))]
|
|
|
|
mc_version: String,
|
|
|
|
/// The modpack's modloader
|
2024-08-13 23:56:57 +01:00
|
|
|
#[arg(long, default_value_t = modpack::ModLoader::Fabric)]
|
|
|
|
modloader: modpack::ModLoader,
|
2024-08-14 22:45:09 +01:00
|
|
|
/// Default providers to download the mods from for the modpack (can be overridden on a per-mod basis)
|
|
|
|
#[arg(long)]
|
|
|
|
providers: Vec<ModProvider>,
|
2024-08-13 23:56:57 +01:00
|
|
|
},
|
2024-08-14 22:32:45 +01:00
|
|
|
/// Add a new mod to the modpack
|
|
|
|
Add {
|
|
|
|
/// Name of the mod to add to the project, optionally including a version
|
|
|
|
name: String,
|
|
|
|
/// Providers to download the mods from
|
|
|
|
#[arg(long)]
|
|
|
|
providers: Vec<ModProvider>,
|
|
|
|
/// URL to download the mod from
|
|
|
|
#[arg(long)]
|
2024-08-14 22:45:09 +01:00
|
|
|
url: Option<String>,
|
2024-08-18 22:56:51 +01:00
|
|
|
/// Use exact transitive mod dependency versions
|
2024-08-18 00:45:14 +01:00
|
|
|
#[arg(long, short, action)]
|
2024-08-18 22:56:51 +01:00
|
|
|
locked: bool,
|
2024-08-18 01:16:23 +01:00
|
|
|
/// Minecraft version override
|
2024-08-18 23:05:44 +01:00
|
|
|
#[arg(long)]
|
2024-08-18 01:16:23 +01:00
|
|
|
mc_version: Option<String>,
|
|
|
|
/// Modloader override
|
|
|
|
#[arg(long, short)]
|
|
|
|
modloader: Option<modpack::ModLoader>,
|
2024-08-14 22:45:09 +01:00
|
|
|
},
|
2024-08-17 23:38:40 +01:00
|
|
|
/// Remove a mod from the modpack
|
|
|
|
Remove {
|
|
|
|
/// Name of the mod to remove from the modpack
|
|
|
|
name: String,
|
2024-08-19 00:24:36 +01:00
|
|
|
/// Forcefully remove the mod without checking if anything depends on it
|
|
|
|
#[arg(long, short, action)]
|
|
|
|
force: bool,
|
2024-08-17 23:38:40 +01:00
|
|
|
},
|
2024-08-19 00:50:38 +01:00
|
|
|
/// Forbid a mod from the modpack
|
|
|
|
Forbid {
|
|
|
|
/// Name of the mod to remove and forbid from the modpack
|
|
|
|
name: String,
|
|
|
|
},
|
2024-08-18 22:25:01 +01:00
|
|
|
/// Download the mods in the pack to a specified folder
|
|
|
|
Download {
|
|
|
|
/// Mods directory
|
|
|
|
mods_dir: PathBuf,
|
2024-08-18 23:32:23 +01:00
|
|
|
/// Side to download for
|
2024-08-20 20:00:43 +01:00
|
|
|
#[arg(long, default_value_t = DownloadSide::Server)]
|
2024-08-18 23:32:23 +01:00
|
|
|
side: DownloadSide,
|
2024-08-20 01:20:17 +01:00
|
|
|
/// Download mods from a remote modpack in a git repo
|
|
|
|
#[arg(long)]
|
|
|
|
git: Option<String>,
|
|
|
|
/// Download mods from a local modpack
|
|
|
|
#[arg(long)]
|
|
|
|
path: Option<PathBuf>,
|
2024-08-18 22:25:01 +01:00
|
|
|
},
|
2024-08-19 01:08:27 +01:00
|
|
|
/// Update all mods to the latest possible version
|
2024-08-18 23:38:30 +01:00
|
|
|
Update {
|
|
|
|
/// Use exact transitive mod dependency versions
|
|
|
|
#[arg(long, short, action)]
|
|
|
|
locked: bool,
|
2024-08-19 00:24:36 +01:00
|
|
|
},
|
2024-08-20 20:00:43 +01:00
|
|
|
/// Manage mcmpmgr profiles
|
|
|
|
Profile(ProfileArgs),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Args)]
|
|
|
|
#[command(args_conflicts_with_subcommands = true)]
|
|
|
|
struct ProfileArgs {
|
|
|
|
#[command(subcommand)]
|
|
|
|
command: Option<ProfileCommands>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Subcommand)]
|
|
|
|
enum ProfileCommands {
|
|
|
|
/// List all profiles
|
|
|
|
List,
|
|
|
|
/// Add or overwrite a profile
|
|
|
|
Add {
|
|
|
|
/// Name of the profile
|
|
|
|
name: String,
|
|
|
|
/// Side to download the profile for. (Client, Server, or Both)
|
|
|
|
#[arg(long, default_value_t = DownloadSide::Server)]
|
|
|
|
side: DownloadSide,
|
|
|
|
/// A local file path to a modpack directory or a git repo url prefixed with 'git+'
|
|
|
|
#[arg(long, short)]
|
|
|
|
pack_source: PackSource,
|
|
|
|
/// Mods directory
|
|
|
|
#[arg(long, short)]
|
|
|
|
mods_directory: PathBuf,
|
|
|
|
},
|
|
|
|
/// Install a profile
|
|
|
|
Install {
|
|
|
|
/// Name of the profile to install
|
|
|
|
name: String,
|
|
|
|
},
|
|
|
|
/// Show information about a profile
|
|
|
|
Show {
|
|
|
|
/// Name of the profile to show
|
|
|
|
name: String,
|
|
|
|
},
|
|
|
|
/// Delete a profile
|
|
|
|
Remove {
|
|
|
|
/// Profile to remove
|
|
|
|
name: String,
|
|
|
|
},
|
2024-08-13 23:56:57 +01:00
|
|
|
}
|
|
|
|
|
2024-08-17 16:11:04 +01:00
|
|
|
#[tokio::main(flavor = "multi_thread")]
|
|
|
|
async fn main() -> Result<(), Box<dyn Error>> {
|
2024-08-13 23:56:57 +01:00
|
|
|
let cli = Cli::parse();
|
|
|
|
|
|
|
|
if let Some(command) = cli.command {
|
|
|
|
match command {
|
|
|
|
Commands::Init {
|
|
|
|
directory,
|
|
|
|
mc_version,
|
|
|
|
modloader,
|
2024-08-14 21:28:40 +01:00
|
|
|
name,
|
2024-08-14 22:45:09 +01:00
|
|
|
providers,
|
2024-08-13 23:56:57 +01:00
|
|
|
} => {
|
2024-08-14 20:51:42 +01:00
|
|
|
let dir = directory.unwrap_or(std::env::current_dir()?);
|
2024-08-14 21:28:40 +01:00
|
|
|
let pack_name = if let Some(name) = name {
|
|
|
|
name
|
|
|
|
} else {
|
|
|
|
dir.file_name()
|
|
|
|
.ok_or(format!(
|
|
|
|
"Cannot find pack name based on directory '{}'",
|
|
|
|
dir.display()
|
|
|
|
))?
|
|
|
|
.to_string_lossy()
|
|
|
|
.into()
|
|
|
|
};
|
|
|
|
println!(
|
|
|
|
"Initializing project '{}' at '{}'...",
|
|
|
|
&pack_name,
|
|
|
|
dir.display()
|
|
|
|
);
|
2024-08-14 22:45:09 +01:00
|
|
|
let mut mc_modpack_meta: ModpackMeta =
|
2024-08-14 21:28:40 +01:00
|
|
|
ModpackMeta::new(&pack_name, &mc_version, modloader);
|
2024-08-14 22:45:09 +01:00
|
|
|
for provider in providers.into_iter() {
|
|
|
|
mc_modpack_meta = mc_modpack_meta.provider(provider);
|
|
|
|
}
|
2024-08-14 21:28:40 +01:00
|
|
|
mc_modpack_meta.init_project(&dir)?;
|
2024-08-18 01:16:23 +01:00
|
|
|
let modpack_lock =
|
2024-08-18 23:03:18 +01:00
|
|
|
resolver::PinnedPackMeta::load_from_directory(&dir, true).await?;
|
2024-08-17 21:06:31 +01:00
|
|
|
modpack_lock.save_to_dir(&dir)?;
|
2024-08-14 21:28:40 +01:00
|
|
|
}
|
|
|
|
Commands::New {
|
|
|
|
name,
|
|
|
|
mc_version,
|
|
|
|
modloader,
|
2024-08-14 22:45:09 +01:00
|
|
|
providers,
|
2024-08-14 21:28:40 +01:00
|
|
|
} => {
|
|
|
|
let dir = std::env::current_dir()?.join(PathBuf::from(&name));
|
|
|
|
println!(
|
|
|
|
"Creating new modpack project '{}' at '{}'...",
|
|
|
|
&name,
|
|
|
|
dir.display()
|
|
|
|
);
|
|
|
|
std::fs::create_dir_all(&dir)?;
|
2024-08-14 22:45:09 +01:00
|
|
|
let mut mc_modpack_meta: ModpackMeta =
|
|
|
|
ModpackMeta::new(&name, &mc_version, modloader);
|
|
|
|
for provider in providers.into_iter() {
|
|
|
|
mc_modpack_meta = mc_modpack_meta.provider(provider);
|
|
|
|
}
|
2024-08-14 21:28:40 +01:00
|
|
|
mc_modpack_meta.init_project(&dir)?;
|
2024-08-17 23:38:40 +01:00
|
|
|
|
2024-08-18 01:16:23 +01:00
|
|
|
let modpack_lock =
|
2024-08-18 23:03:18 +01:00
|
|
|
resolver::PinnedPackMeta::load_from_directory(&dir, true).await?;
|
2024-08-17 21:06:31 +01:00
|
|
|
modpack_lock.save_to_dir(&dir)?;
|
2024-08-13 23:56:57 +01:00
|
|
|
}
|
2024-08-14 22:45:09 +01:00
|
|
|
Commands::Add {
|
|
|
|
name,
|
|
|
|
providers,
|
|
|
|
url,
|
2024-08-18 22:56:51 +01:00
|
|
|
locked,
|
2024-08-18 01:16:23 +01:00
|
|
|
mc_version,
|
|
|
|
modloader,
|
2024-08-14 22:45:09 +01:00
|
|
|
} => {
|
2024-08-14 22:32:45 +01:00
|
|
|
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
2024-08-17 16:11:04 +01:00
|
|
|
let old_modpack_meta = modpack_meta.clone();
|
2024-08-14 22:32:45 +01:00
|
|
|
|
|
|
|
let mut mod_meta = ModMeta::new(&name)?;
|
2024-08-18 01:16:23 +01:00
|
|
|
|
|
|
|
if let Some(modloader) = modloader {
|
|
|
|
mod_meta = mod_meta.modloader(modloader);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(mc_version) = mc_version {
|
|
|
|
mod_meta = mod_meta.mc_version(&mc_version);
|
|
|
|
}
|
|
|
|
|
2024-08-14 22:32:45 +01:00
|
|
|
if let Some(url) = url {
|
|
|
|
mod_meta = mod_meta.url(&url);
|
|
|
|
}
|
|
|
|
for provider in providers.into_iter() {
|
|
|
|
mod_meta = mod_meta.provider(provider);
|
|
|
|
}
|
2024-08-19 00:50:38 +01:00
|
|
|
modpack_meta = modpack_meta.add_mod(&mod_meta)?;
|
2024-08-14 22:32:45 +01:00
|
|
|
modpack_meta.save_current_dir_project()?;
|
2024-08-16 00:31:02 +01:00
|
|
|
|
2024-08-17 16:11:04 +01:00
|
|
|
let revert_modpack_meta = |e| -> ! {
|
|
|
|
let revert_result = old_modpack_meta.save_current_dir_project();
|
|
|
|
if let Err(result) = revert_result {
|
|
|
|
panic!("Failed to revert modpack meta: {}", result);
|
|
|
|
}
|
|
|
|
panic!("Reverted modpack meta:\n{}", e);
|
|
|
|
};
|
|
|
|
|
2024-08-18 23:32:23 +01:00
|
|
|
match resolver::PinnedPackMeta::load_from_current_directory(!locked).await {
|
2024-08-17 16:11:04 +01:00
|
|
|
Ok(mut modpack_lock) => {
|
2024-08-19 00:24:36 +01:00
|
|
|
let remove_result =
|
|
|
|
modpack_lock.remove_mod(&mod_meta.name, &modpack_meta, true);
|
2024-08-18 01:24:05 +01:00
|
|
|
if let Err(e) = remove_result {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
|
2024-08-17 23:38:40 +01:00
|
|
|
let pin_result = modpack_lock
|
2024-08-18 22:56:51 +01:00
|
|
|
.pin_mod_and_deps(&mod_meta, &modpack_meta, !locked)
|
2024-08-17 23:38:40 +01:00
|
|
|
.await;
|
2024-08-17 16:11:04 +01:00
|
|
|
if let Err(e) = pin_result {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Err(e) = modpack_lock.save_current_dir_lock() {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
};
|
2024-08-14 22:45:09 +01:00
|
|
|
}
|
2024-08-19 00:24:36 +01:00
|
|
|
Commands::Remove { name, force } => {
|
2024-08-17 23:38:40 +01:00
|
|
|
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
|
|
|
let old_modpack_meta = modpack_meta.clone();
|
|
|
|
|
|
|
|
modpack_meta = modpack_meta.remove_mod(&name);
|
|
|
|
modpack_meta.save_current_dir_project()?;
|
|
|
|
|
|
|
|
let revert_modpack_meta = |e| -> ! {
|
|
|
|
let revert_result = old_modpack_meta.save_current_dir_project();
|
|
|
|
if let Err(result) = revert_result {
|
|
|
|
panic!("Failed to revert modpack meta: {}", result);
|
|
|
|
}
|
|
|
|
panic!("Reverted modpack meta:\n{}", e);
|
|
|
|
};
|
|
|
|
|
2024-08-18 23:03:18 +01:00
|
|
|
match resolver::PinnedPackMeta::load_from_current_directory(true).await {
|
2024-08-17 23:38:40 +01:00
|
|
|
Ok(mut modpack_lock) => {
|
2024-08-19 00:24:36 +01:00
|
|
|
let remove_result = modpack_lock.remove_mod(&name, &modpack_meta, force);
|
2024-08-17 23:38:40 +01:00
|
|
|
if let Err(e) = remove_result {
|
2024-08-19 00:50:38 +01:00
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Err(e) = modpack_lock.save_current_dir_lock() {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
Commands::Forbid { name } => {
|
|
|
|
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
|
|
|
let old_modpack_meta = modpack_meta.clone();
|
|
|
|
|
|
|
|
modpack_meta.forbid_mod(&name);
|
|
|
|
modpack_meta = modpack_meta.remove_mod(&name);
|
|
|
|
modpack_meta.save_current_dir_project()?;
|
|
|
|
|
|
|
|
let revert_modpack_meta = |e| -> ! {
|
|
|
|
let revert_result = old_modpack_meta.save_current_dir_project();
|
|
|
|
if let Err(result) = revert_result {
|
|
|
|
panic!("Failed to revert modpack meta: {}", result);
|
|
|
|
}
|
|
|
|
panic!("Reverted modpack meta:\n{}", e);
|
|
|
|
};
|
|
|
|
|
|
|
|
match resolver::PinnedPackMeta::load_from_current_directory(true).await {
|
|
|
|
Ok(mut modpack_lock) => {
|
|
|
|
let remove_result = modpack_lock.remove_mod(&name, &modpack_meta, true);
|
|
|
|
if let Err(e) = remove_result {
|
2024-08-17 23:38:40 +01:00
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Err(e) = modpack_lock.save_current_dir_lock() {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
revert_modpack_meta(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2024-08-20 01:20:17 +01:00
|
|
|
Commands::Download {
|
|
|
|
mods_dir,
|
|
|
|
side,
|
|
|
|
git,
|
|
|
|
path,
|
|
|
|
} => {
|
2024-08-20 01:34:22 +01:00
|
|
|
let mut pack_dir: Option<tempfile::TempDir> = None;
|
2024-08-20 01:20:17 +01:00
|
|
|
let pack_lock = if let Some(git_url) = git {
|
2024-08-20 01:34:22 +01:00
|
|
|
let (lock_meta, repo_dir) =
|
|
|
|
resolver::PinnedPackMeta::load_from_git_repo(&git_url, true).await?;
|
2024-08-20 20:00:43 +01:00
|
|
|
// Hold on to the repo directory until pack_dir is dropped
|
|
|
|
let _ = pack_dir.insert(repo_dir);
|
|
|
|
lock_meta
|
2024-08-20 01:20:17 +01:00
|
|
|
} else if let Some(local_path) = path {
|
|
|
|
resolver::PinnedPackMeta::load_from_directory(&local_path, true).await?
|
|
|
|
} else {
|
|
|
|
resolver::PinnedPackMeta::load_from_current_directory(true).await?
|
|
|
|
};
|
|
|
|
|
2024-08-18 23:32:23 +01:00
|
|
|
pack_lock.download_mods(&mods_dir, side).await?;
|
2024-08-18 22:25:01 +01:00
|
|
|
println!("Mods updated");
|
|
|
|
}
|
2024-08-18 23:38:30 +01:00
|
|
|
Commands::Update { locked } => {
|
|
|
|
let mut pack_lock = resolver::PinnedPackMeta::new();
|
|
|
|
let modpack_meta = ModpackMeta::load_from_current_directory()?;
|
|
|
|
pack_lock.init(&modpack_meta, !locked).await?;
|
|
|
|
pack_lock.save_current_dir_lock()?;
|
|
|
|
}
|
2024-08-20 20:00:43 +01:00
|
|
|
Commands::Profile(ProfileArgs { command }) => {
|
|
|
|
if let Some(command) = command {
|
|
|
|
match command {
|
|
|
|
ProfileCommands::List => {
|
|
|
|
let userdata = profiles::Data::load()?;
|
|
|
|
println!("Profiles:");
|
|
|
|
for profile in userdata.get_profile_names().iter() {
|
|
|
|
println!("- {profile}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ProfileCommands::Add {
|
|
|
|
name,
|
|
|
|
side,
|
|
|
|
pack_source,
|
|
|
|
mods_directory,
|
|
|
|
} => {
|
|
|
|
let mut userdata = profiles::Data::load()?;
|
|
|
|
let profile = Profile::new(&mods_directory, pack_source, side);
|
|
|
|
userdata.add_profile(&name, profile);
|
|
|
|
userdata.save()?;
|
|
|
|
println!("Saved profile '{name}'");
|
|
|
|
}
|
|
|
|
ProfileCommands::Install { name } => {
|
|
|
|
let userdata = profiles::Data::load()?;
|
|
|
|
let profile = userdata
|
|
|
|
.get_profile(&name)
|
|
|
|
.ok_or(format!("Profile '{name}' does not exist"))?;
|
|
|
|
println!("Installing profile '{name}'...");
|
|
|
|
profile.install().await?;
|
|
|
|
println!("Installed profile '{name}' successfully");
|
|
|
|
}
|
|
|
|
ProfileCommands::Remove { name } => {
|
|
|
|
let mut userdata = profiles::Data::load()?;
|
|
|
|
userdata.remove_profile(&name);
|
|
|
|
println!("Removed profile '{name}'");
|
|
|
|
}
|
|
|
|
ProfileCommands::Show { name } => {
|
|
|
|
let userdata = profiles::Data::load()?;
|
|
|
|
let profile = userdata
|
|
|
|
.get_profile(&name)
|
|
|
|
.ok_or(format!("Profile '{name}' does not exist"))?;
|
|
|
|
println!("Profile name : {name}");
|
|
|
|
println!("Mods folder : {}", profile.mods_folder.display());
|
|
|
|
println!("Modpack source: {}", profile.pack_source);
|
|
|
|
println!("Side : {}", profile.side);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-08-13 23:56:57 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|