lux_cli/
install_rockspec.rs

1use eyre::eyre;
2use itertools::Itertools;
3use std::{path::PathBuf, sync::Arc};
4
5use clap::Args;
6use eyre::Result;
7use lux_lib::{
8    build::{self, BuildBehaviour},
9    config::Config,
10    lockfile::{OptState, PinnedState},
11    lua_rockspec::{BuildBackendSpec, RemoteLuaRockspec},
12    luarocks::luarocks_installation::LuaRocksInstallation,
13    operations::{Install, PackageInstallSpec},
14    package::PackageName,
15    progress::MultiProgress,
16    rockspec::{LuaVersionCompatibility, Rockspec},
17    tree,
18};
19
20#[derive(Args, Default)]
21pub struct InstallRockspec {
22    /// The path to the RockSpec file to install
23    rockspec_path: PathBuf,
24
25    /// Whether to pin the installed package and dependencies.
26    #[arg(long)]
27    pin: bool,
28}
29
30/// Install a rockspec into the user tree.
31pub async fn install_rockspec(data: InstallRockspec, config: Config) -> Result<()> {
32    let pin = PinnedState::from(data.pin);
33    let path = data.rockspec_path;
34
35    if path
36        .extension()
37        .map(|ext| ext != "rockspec")
38        .unwrap_or(true)
39    {
40        return Err(eyre!("Provided path is not a valid rockspec!"));
41    }
42    let content = std::fs::read_to_string(path)?;
43    let rockspec = RemoteLuaRockspec::new(&content)?;
44    let lua_version = rockspec.lua_version_matches(&config)?;
45    let tree = config.user_tree(lua_version)?;
46
47    // Ensure all dependencies are installed first
48    let dependencies = rockspec
49        .dependencies()
50        .current_platform()
51        .iter()
52        .filter(|package| !package.name().eq(&PackageName::new("lua".into())))
53        .collect_vec();
54
55    let progress_arc = MultiProgress::new_arc();
56    let progress = Arc::clone(&progress_arc);
57
58    let dependencies_to_install = dependencies
59        .into_iter()
60        .filter(|dep| {
61            tree.match_rocks(dep.package_req())
62                .is_ok_and(|rock_match| rock_match.is_found())
63        })
64        .map(|dep| {
65            PackageInstallSpec::new(dep.package_req().clone(), tree::EntryType::DependencyOnly)
66                .build_behaviour(BuildBehaviour::NoForce)
67                .pin(pin)
68                .opt(OptState::Required)
69                .maybe_source(dep.source().clone())
70                .build()
71        })
72        .collect();
73
74    Install::new(&config)
75        .packages(dependencies_to_install)
76        .tree(tree.clone())
77        .progress(progress_arc.clone())
78        .install()
79        .await?;
80
81    if let Some(BuildBackendSpec::LuaRock(build_backend)) =
82        &rockspec.build().current_platform().build_backend
83    {
84        let build_tree = tree.build_tree(&config)?;
85        let luarocks = LuaRocksInstallation::new(&config, build_tree)?;
86        let bar = progress.map(|p| p.new_bar());
87        luarocks.ensure_installed(&bar).await?;
88        luarocks
89            .install_build_dependencies(build_backend, &rockspec, progress_arc.clone())
90            .await?;
91    }
92
93    build::Build::new(
94        &rockspec,
95        &tree,
96        tree::EntryType::Entrypoint,
97        &config,
98        &progress.map(|p| p.new_bar()),
99    )
100    .pin(pin)
101    .behaviour(BuildBehaviour::Force)
102    .build()
103    .await?;
104
105    Ok(())
106}