Skip to main content

lux_lib/operations/
install_project.rs

1use crate::{
2    build::{Build, BuildBehaviour, BuildError},
3    config::Config,
4    lockfile::LocalPackage,
5    lua_installation::{LuaInstallation, LuaInstallationError},
6    luarocks::luarocks_installation::{LuaRocksError, LuaRocksInstallError, LuaRocksInstallation},
7    operations::{install_dependencies::prepare_dependencies_for_build, InstallDependencies},
8    progress::{MultiProgress, Progress},
9    project::{project_toml::LocalProjectTomlValidationError, Project, ProjectError},
10    tree::{self, InstallTree, TreeError},
11};
12use bon::Builder;
13use itertools::Itertools;
14use miette::Diagnostic;
15use std::sync::Arc;
16use thiserror::Error;
17
18use super::InstallError;
19
20#[derive(Debug, Error, Diagnostic)]
21pub enum InstallProjectError {
22    #[error(transparent)]
23    #[diagnostic(transparent)]
24    LocalProjectTomlValidation(#[from] LocalProjectTomlValidationError),
25    #[error(transparent)]
26    #[diagnostic(transparent)]
27    Project(#[from] ProjectError),
28    #[error(transparent)]
29    #[diagnostic(transparent)]
30    LuaInstallation(#[from] LuaInstallationError),
31    #[error(transparent)]
32    #[diagnostic(transparent)]
33    Tree(#[from] TreeError),
34    #[error(transparent)]
35    #[diagnostic(transparent)]
36    LuaRocks(#[from] LuaRocksError),
37    #[error(transparent)]
38    #[diagnostic(transparent)]
39    LuaRocksInstall(#[from] LuaRocksInstallError),
40    #[error("error installind dependencies:\n{0}")]
41    #[diagnostic(forward(0))]
42    InstallDependencies(InstallError),
43    #[error("error installind build dependencies:\n{0}")]
44    #[diagnostic(forward(0))]
45    InstallBuildDependencies(InstallError),
46    #[error("error building project:\n{0}")]
47    #[diagnostic(forward(0))]
48    Build(#[from] BuildError),
49}
50
51/// Installs a project into a [`Tree`].
52/// Typically, you will want to use [`crate::operations::BuildWorkspace`].
53/// Useful for installing a project and its dependencies outside of a workspace tree.
54#[derive(Builder)]
55#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
56pub struct InstallProject<'a, T>
57where
58    T: InstallTree,
59{
60    project: &'a Project,
61
62    config: &'a Config,
63
64    tree: &'a T,
65
66    progress: Option<Arc<Progress<MultiProgress>>>,
67}
68
69impl<
70        T: InstallTree + Sync + Send + Clone + 'static,
71        State: install_project_builder::State + install_project_builder::IsComplete,
72    > InstallProjectBuilder<'_, T, State>
73{
74    /// Returns `Some` if the `only_deps` option is set to `false`.
75    pub async fn build(self) -> Result<LocalPackage, InstallProjectError> {
76        let args = self._build();
77        let config = args.config;
78        let project = args.project;
79        let tree = args.tree;
80        let build_tree = tree.build_tree(config)?;
81        let progress_arc = args
82            .progress
83            .clone()
84            .unwrap_or_else(|| MultiProgress::new_arc(args.config));
85        let lua = LuaInstallation::new_from_config(
86            config,
87            &progress_arc.map(|progress| progress.new_bar()),
88        )
89        .await?;
90        let luarocks = LuaRocksInstallation::new(config, build_tree.clone())?;
91        let mut dependencies_to_install = Vec::new();
92        let mut build_dependencies_to_install = Vec::new();
93        let project_toml = project.toml().into_local()?;
94        prepare_dependencies_for_build(
95            &project_toml,
96            tree,
97            &mut dependencies_to_install,
98            &mut build_dependencies_to_install,
99        );
100
101        InstallDependencies::new()
102            .dependencies(dependencies_to_install.into_iter().unique().collect_vec())
103            .build_dependencies(
104                build_dependencies_to_install
105                    .into_iter()
106                    .unique()
107                    .collect_vec(),
108            )
109            .tree(tree)
110            .lua(&lua)
111            .luarocks(&luarocks)
112            .config(config)
113            .progress(progress_arc.clone())
114            .build()
115            .await
116            .map_err(InstallProjectError::InstallBuildDependencies)?;
117
118        let package = Build::new()
119            .rockspec(&project_toml)
120            .lua(&lua)
121            .tree(tree)
122            .entry_type(tree::EntryType::Entrypoint)
123            .config(config)
124            .progress(&progress_arc.map(|p| p.new_bar()))
125            .behaviour(BuildBehaviour::Force)
126            .build()
127            .await?;
128
129        let lockfile = tree.lockfile()?;
130        let dependencies = lockfile
131            .rocks()
132            .iter()
133            .filter_map(|(pkg_id, value)| {
134                if lockfile.is_entrypoint(pkg_id) {
135                    Some(value)
136                } else {
137                    None
138                }
139            })
140            .cloned()
141            .collect_vec();
142        let mut lockfile = lockfile.write_guard();
143        lockfile.add_entrypoint(&package);
144        for dep in dependencies {
145            lockfile.add_dependency(&package, &dep);
146            lockfile.remove_entrypoint(&dep);
147        }
148        Ok(package)
149    }
150}