lux_lib/operations/
install_rockspec.rs1use bon::Builder;
2use miette::Diagnostic;
3use thiserror::Error;
4
5use crate::{
6 build::{self, BuildBehaviour, BuildError},
7 config::Config,
8 lockfile::{LocalPackage, OptState, PinnedState},
9 lua_installation::{LuaInstallation, LuaInstallationError},
10 lua_rockspec::{BuildBackendSpec, LuaVersionError, RemoteLuaRockspec},
11 luarocks::luarocks_installation::{LuaRocksError, LuaRocksInstallError, LuaRocksInstallation},
12 operations::{Install, InstallError, PackageInstallSpec},
13 rockspec::{LuaVersionCompatibility, Rockspec},
14 tree::{self, InstallTree, TreeError},
15};
16
17#[derive(Debug, Error, Diagnostic)]
18#[error(transparent)]
19#[non_exhaustive]
20pub enum InstallRockspecError {
21 #[diagnostic(transparent)]
22 LuaInstallation(#[from] LuaInstallationError),
23 #[diagnostic(transparent)]
24 LuaVersion(#[from] LuaVersionError),
25 #[diagnostic(transparent)]
26 Tree(#[from] TreeError),
27 #[diagnostic(transparent)]
28 Install(#[from] InstallError),
29 #[diagnostic(transparent)]
30 LuaRocks(#[from] LuaRocksError),
31 #[diagnostic(transparent)]
32 LuaRocksInstall(#[from] LuaRocksInstallError),
33 #[diagnostic(transparent)]
34 Build(#[from] BuildError),
35}
36
37#[derive(Builder)]
39#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
40pub struct InstallRockspec<'a, T>
41where
42 T: InstallTree,
43{
44 rockspec: RemoteLuaRockspec,
45
46 pin: PinnedState,
47
48 config: &'a Config,
49
50 tree: &'a T,
51}
52
53impl<
54 T: InstallTree + Sync + Send + Clone + 'static,
55 State: install_rockspec_builder::State + install_rockspec_builder::IsComplete,
56 > InstallRockspecBuilder<'_, T, State>
57{
58 pub async fn install(self) -> Result<LocalPackage, InstallRockspecError> {
59 let args = self._build();
60 let rockspec = args.rockspec;
61 let pin = args.pin;
62 let config = args.config;
63 let tree = args.tree;
64
65 let lua_version = rockspec.lua_version_matches(config)?;
66 let lua = LuaInstallation::new(&lua_version, config).await?;
67
68 let build_dependencies = rockspec.build_dependencies().current_platform();
71
72 let build_dependencies_to_install = build_dependencies
73 .iter()
74 .filter(|dep| {
75 !matches!(
77 dep.name().to_string().as_str(),
78 "luarocks-build-rust-mlua"
79 | "luarocks-build-rust-binary"
80 | "luarocks-build-treesitter-parser"
81 )
82 })
83 .filter(|dep| {
84 tree.match_rocks(dep.package_req())
85 .is_ok_and(|rock_match| rock_match.is_found())
86 })
87 .map(|dep| {
88 PackageInstallSpec::new(dep.package_req().clone(), tree::EntryType::Entrypoint)
89 .build_behaviour(BuildBehaviour::NoForce)
90 .pin(pin)
91 .opt(OptState::Required)
92 .maybe_source(dep.source().clone())
93 .build()
94 })
95 .collect();
96
97 Install::new(config)
98 .packages(build_dependencies_to_install)
99 .tree(tree.build_tree(config)?)
100 .install()
101 .await?;
102
103 let dependencies = rockspec.dependencies().current_platform();
104
105 let mut dependencies_to_install = Vec::new();
106 for dep in dependencies {
107 let rock_match = tree.match_rocks(dep.package_req())?;
108 if !rock_match.is_found() {
109 let dep = PackageInstallSpec::new(
110 dep.package_req().clone(),
111 tree::EntryType::DependencyOnly,
112 )
113 .build_behaviour(BuildBehaviour::NoForce)
114 .pin(pin)
115 .opt(OptState::Required)
116 .maybe_source(dep.source().clone())
117 .build();
118 dependencies_to_install.push(dep);
119 }
120 }
121
122 let dependencies = Install::new(config)
123 .packages(dependencies_to_install)
124 .tree(tree.clone())
125 .install()
126 .await?;
127
128 if let Some(BuildBackendSpec::LuaRock(_)) =
129 &rockspec.build().current_platform().build_backend
130 {
131 let build_tree = tree.build_tree(config)?;
132 let luarocks = LuaRocksInstallation::new(config, build_tree)?;
133 luarocks.ensure_installed(&lua).await?;
134 }
135
136 let package = build::Build::new()
137 .rockspec(&rockspec)
138 .tree(tree)
139 .lua(&lua)
140 .entry_type(tree::EntryType::Entrypoint)
141 .config(config)
142 .pin(pin)
143 .behaviour(BuildBehaviour::Force)
144 .build()
145 .await?;
146
147 let lockfile = tree.lockfile()?;
148 let build_lockfile = tree.build_tree(config)?.lockfile()?;
149
150 let mut lockfile = lockfile.write_guard();
151 lockfile.add_entrypoint(&package);
152 for dep in dependencies {
153 lockfile.add_dependency(&package, &dep);
154 }
155 for dep in build_lockfile.rocks().values() {
156 lockfile.add_build_dependency(&package, dep);
157 }
158 Ok(package)
159 }
160}