Skip to main content

lux_lib/operations/
install_project.rs

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