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