Skip to main content

conduit_cli/core/engine/manager/
install_loader.rs

1use crate::{
2    core::{
3        domain::loader::Loader,
4        engine::{io::TomlFile, manager::ProjectManager},
5        schemas::lock::InstanceSnapshot,
6    },
7    errors::ConduitResult,
8};
9
10impl ProjectManager {
11    pub async fn install_loader(&self, loader: Loader) -> ConduitResult<()> {
12        let mut manifest = self.ctx.manifest.read().await.clone();
13        let lock = self.ctx.lockfile.read().await.clone();
14
15        validate_loader_compatibility(&manifest.project.minecraft, &loader)?;
16
17        let resolved = self
18            .resolver
19            .resolve_loader(&loader, &manifest.project.minecraft)
20            .await?;
21
22        let mut active_lock = self.workflow.migration(&manifest, &lock).await?;
23
24        if self
25            .workflow
26            .ensure_loader_presence(&active_lock, &manifest)?
27        {
28            let mut ctx_lock = self.ctx.lockfile.write().await;
29            *ctx_lock = active_lock;
30            return Ok(());
31        }
32
33        let (final_hash, kind) = self.workflow.download_loader(&resolved).await?;
34
35        self.workflow
36            .execute_installation(
37                &resolved,
38                &final_hash,
39                kind,
40                &loader,
41                &manifest.project.minecraft,
42            )
43            .await?;
44
45        active_lock.instance = InstanceSnapshot {
46            minecraft_version: manifest.project.minecraft.clone(),
47            loader: loader.clone(),
48            loader_hash: Some(final_hash),
49            hash_kind: Some(kind),
50        };
51
52        manifest.project.loader = loader;
53
54        manifest.save(self.ctx.paths.manifest()).await?;
55        active_lock.save(self.ctx.paths.lock()).await?;
56
57        let mut ctx_manifest = self.ctx.manifest.write().await;
58        *ctx_manifest = manifest;
59
60        let mut ctx_lock = self.ctx.lockfile.write().await;
61        *ctx_lock = active_lock;
62
63        Ok(())
64    }
65}
66
67fn validate_loader_compatibility(mc_version: &str, loader: &Loader) -> ConduitResult<()> {
68    match loader {
69        Loader::Forge { version } => {
70            if !version.contains(mc_version) {
71                return Err(crate::errors::ConduitError::Validation(format!(
72                    "Forge {version} is not compatible with Minecraft {mc_version}"
73                )));
74            }
75        }
76        Loader::Neoforge { version } => {
77            let mc_parts: Vec<&str> = mc_version.split('.').collect();
78            let nf_parts: Vec<&str> = version.split('.').collect();
79
80            if let (Some(mc_minor), Some(nf_major)) = (mc_parts.get(1), nf_parts.first())
81                && mc_minor != nf_major
82            {
83                return Err(crate::errors::ConduitError::Validation(format!(
84                    "NeoForge {version} is not compatible with Minecraft {mc_version}"
85                )));
86            }
87        }
88        _ => {}
89    }
90    Ok(())
91}