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