lux_cli/
install_rockspec.rs

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