conduit_cli/core/engine/manager/
install_loader.rs1use 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}