use async_trait::async_trait;
use std::{path::{Path, PathBuf}, sync::Arc};
use crate::error::Result;
use crate::install::vanilla::LoaderType;
pub type ProgressFn = Arc<dyn Fn(usize, usize, &str) + Send + Sync + 'static>;
pub fn no_progress() -> ProgressFn {
Arc::new(|_, _, _| {})
}
#[async_trait]
pub trait LoaderInstaller: Send + Sync {
fn loader_type(&self) -> LoaderType;
async fn available_versions(&self, mc_version: &str) -> Result<Vec<String>>;
async fn install(
&self,
mc_version: &str,
instance_name: &str,
base_dir: &Path,
progress: ProgressFn,
) -> Result<()>;
}
pub struct VanillaInstaller;
#[async_trait]
impl LoaderInstaller for VanillaInstaller {
fn loader_type(&self) -> LoaderType {
LoaderType::Vanilla
}
async fn available_versions(&self, _mc_version: &str) -> Result<Vec<String>> {
Ok(vec![])
}
async fn install(
&self,
mc_version: &str,
instance_name: &str,
base_dir: &Path,
progress: ProgressFn,
) -> Result<()> {
crate::install::vanilla::install_vanilla(mc_version, instance_name, base_dir, move |d, t, s| {
progress(d, t, s)
})
.await?;
Ok(())
}
}
pub struct FabricInstaller {
pub loader_version: Option<String>,
}
impl FabricInstaller {
pub fn new(loader_version: impl Into<String>) -> Self {
Self { loader_version: Some(loader_version.into()) }
}
pub fn latest() -> Self {
Self { loader_version: None }
}
}
#[async_trait]
impl LoaderInstaller for FabricInstaller {
fn loader_type(&self) -> LoaderType {
LoaderType::Fabric
}
async fn available_versions(&self, mc_version: &str) -> Result<Vec<String>> {
let versions = crate::install::fabric::get_fabric_loader_versions(mc_version).await?;
Ok(versions.into_iter().map(|v| v.version).collect())
}
async fn install(
&self,
mc_version: &str,
instance_name: &str,
base_dir: &Path,
_progress: ProgressFn,
) -> Result<()> {
let loader_ver = match &self.loader_version {
Some(v) => v.clone(),
None => {
let versions =
crate::install::fabric::get_fabric_loader_versions(mc_version).await?;
versions
.into_iter()
.find(|v| v.stable)
.map(|v| v.version)
.unwrap_or_default()
}
};
crate::install::fabric::install_fabric(mc_version, &loader_ver, instance_name, base_dir).await
}
}
pub struct ForgeInstaller {
pub forge_version: Option<String>,
pub java_path: PathBuf,
}
impl ForgeInstaller {
pub fn new(java_path: impl Into<PathBuf>) -> Self {
Self { forge_version: None, java_path: java_path.into() }
}
pub fn with_version(java_path: impl Into<PathBuf>, version: impl Into<String>) -> Self {
Self { forge_version: Some(version.into()), java_path: java_path.into() }
}
}
#[async_trait]
impl LoaderInstaller for ForgeInstaller {
fn loader_type(&self) -> LoaderType {
LoaderType::Forge
}
async fn available_versions(&self, mc_version: &str) -> Result<Vec<String>> {
crate::install::forge::get_forge_versions(mc_version).await
}
async fn install(
&self,
mc_version: &str,
instance_name: &str,
base_dir: &Path,
_progress: ProgressFn,
) -> Result<()> {
crate::install::forge::install_forge(
mc_version,
self.forge_version.as_deref(),
instance_name,
&self.java_path,
base_dir,
)
.await
}
}
pub struct NeoForgeInstaller {
pub neoforge_version: Option<String>,
pub java_path: PathBuf,
}
impl NeoForgeInstaller {
pub fn new(java_path: impl Into<PathBuf>) -> Self {
Self { neoforge_version: None, java_path: java_path.into() }
}
pub fn with_version(java_path: impl Into<PathBuf>, version: impl Into<String>) -> Self {
Self { neoforge_version: Some(version.into()), java_path: java_path.into() }
}
}
#[async_trait]
impl LoaderInstaller for NeoForgeInstaller {
fn loader_type(&self) -> LoaderType {
LoaderType::NeoForge
}
async fn available_versions(&self, mc_version: &str) -> Result<Vec<String>> {
crate::install::neoforge::get_neoforge_versions(mc_version).await
}
async fn install(
&self,
mc_version: &str,
instance_name: &str,
base_dir: &Path,
_progress: ProgressFn,
) -> Result<()> {
crate::install::neoforge::install_neoforge(
mc_version,
self.neoforge_version.as_deref(),
instance_name,
&self.java_path,
base_dir,
)
.await
}
}
pub fn create_loader(
loader_type: LoaderType,
java_path: Option<PathBuf>,
) -> Box<dyn LoaderInstaller> {
match loader_type {
LoaderType::Vanilla => Box::new(VanillaInstaller),
LoaderType::Fabric => Box::new(FabricInstaller::latest()),
LoaderType::Forge => Box::new(ForgeInstaller::new(java_path.unwrap_or_default())),
LoaderType::NeoForge => Box::new(NeoForgeInstaller::new(java_path.unwrap_or_default())),
}
}
pub async fn install_with_loader(
mc_version: &str,
instance_name: &str,
base_dir: &Path,
loader: &dyn LoaderInstaller,
progress: ProgressFn,
) -> Result<()> {
if loader.loader_type() != LoaderType::Vanilla {
VanillaInstaller
.install(mc_version, instance_name, base_dir, progress.clone())
.await?;
}
loader.install(mc_version, instance_name, base_dir, progress).await
}