conduit_cli/core/engine/manager/
add.rs1use futures_util::stream::{self, StreamExt};
2use std::sync::Arc;
3
4use crate::{
5 core::{
6 domain::addon::AddonType,
7 engine::{io::TomlFile, manager::ProjectManager, resolver::addon::ResolvedAddon},
8 },
9 errors::ConduitResult,
10};
11
12impl ProjectManager {
13 pub async fn add_addons(
14 &self,
15 identifiers: Vec<String>,
16 target_type: AddonType,
17 ) -> ConduitResult<()> {
18 let (mc_version, loader) = {
19 let manifest = self.ctx.manifest.read().await;
20 self.workflow
21 .validate_compatibility(&target_type, &manifest)?;
22 (
23 manifest.project.minecraft.clone(),
24 manifest.project.loader.clone(),
25 )
26 };
27
28 let resolved_results = stream::iter(identifiers.clone())
29 .map(|id| {
30 let resolver = &self.resolver;
31 let mc = mc_version.clone();
32 let lo = loader.clone();
33 let tt = target_type.clone();
34 async move { resolver.resolve_recursively(&id, &mc, &lo, tt).await }
35 })
36 .buffer_unordered(4)
37 .collect::<Vec<ConduitResult<Vec<ResolvedAddon>>>>()
38 .await;
39
40 let mut all_resolved = Vec::new();
41 for res in resolved_results {
42 all_resolved.extend(res?);
43 }
44
45 let id_map = Arc::new(self.workflow.prepare_addon_id(&all_resolved).await?);
46
47 for id in &identifiers {
48 if let Some(primary) = all_resolved.iter().find(|r| &r.slug == id || &r.id == id) {
49 self.workflow.update_manifest_addons(primary).await?;
50 }
51 }
52
53 let workflow = Arc::new(&self.workflow);
54 stream::iter(all_resolved)
55 .map(|resolved| {
56 let w = Arc::clone(&workflow);
57 let ids = Arc::clone(&id_map);
58 async move { w.install_addon_component(resolved, &ids).await }
59 })
60 .buffer_unordered(8)
61 .collect::<Vec<ConduitResult<()>>>()
62 .await
63 .into_iter()
64 .collect::<ConduitResult<Vec<()>>>()?;
65
66 let lock_path = self.ctx.paths.lock();
67 let lockfile = self.ctx.lockfile.read().await;
68 lockfile.save(lock_path).await?;
69
70 Ok(())
71 }
72}