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] Box<InstallError>),
29 #[diagnostic(transparent)]
30 LuaRocks(#[from] LuaRocksError),
31 #[diagnostic(transparent)]
32 LuaRocksInstall(#[from] Box<LuaRocksInstallError>),
33 #[diagnostic(transparent)]
34 Build(#[from] Box<BuildError>),
35}
36
37impl From<InstallError> for InstallRockspecError {
38 fn from(source: InstallError) -> Self {
39 Self::Install(Box::new(source))
40 }
41}
42
43impl From<LuaRocksInstallError> for InstallRockspecError {
44 fn from(source: LuaRocksInstallError) -> Self {
45 Self::LuaRocksInstall(Box::new(source))
46 }
47}
48
49impl From<BuildError> for InstallRockspecError {
50 fn from(source: BuildError) -> Self {
51 Self::Build(Box::new(source))
52 }
53}
54
55#[derive(Builder)]
57#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
58pub struct InstallRockspec<'a, T>
59where
60 T: InstallTree,
61{
62 rockspec: RemoteLuaRockspec,
63
64 pin: PinnedState,
65
66 config: &'a Config,
67
68 tree: &'a T,
69}
70
71impl<
72 T: InstallTree + Sync + Send + Clone + 'static,
73 State: install_rockspec_builder::State + install_rockspec_builder::IsComplete,
74 > InstallRockspecBuilder<'_, T, State>
75{
76 pub async fn install(self) -> Result<LocalPackage, InstallRockspecError> {
77 let args = self._build();
78 let rockspec = args.rockspec;
79 let pin = args.pin;
80 let config = args.config;
81 let tree = args.tree;
82
83 let lua_version = rockspec.lua_version_matches(config)?;
84 let lua = LuaInstallation::new(&lua_version, config).await?;
85
86 let build_dependencies = rockspec.build_dependencies().current_platform();
89
90 let build_dependencies_to_install = build_dependencies
91 .iter()
92 .filter(|dep| {
93 !matches!(
95 dep.name().to_string().as_str(),
96 "luarocks-build-rust-mlua"
97 | "luarocks-build-rust-binary"
98 | "luarocks-build-treesitter-parser"
99 )
100 })
101 .filter(|dep| {
102 tree.match_rocks(dep.package_req())
103 .is_ok_and(|rock_match| rock_match.is_found())
104 })
105 .map(|dep| {
106 PackageInstallSpec::new(dep.package_req().clone(), tree::EntryType::Entrypoint)
107 .build_behaviour(BuildBehaviour::NoForce)
108 .pin(pin)
109 .opt(OptState::Required)
110 .maybe_source(dep.source().clone())
111 .build()
112 })
113 .collect();
114
115 Install::new(config)
116 .packages(build_dependencies_to_install)
117 .tree(tree.build_tree(config)?)
118 .install()
119 .await?;
120
121 let dependencies = rockspec.dependencies().current_platform();
122
123 let mut dependencies_to_install = Vec::new();
124 for dep in dependencies {
125 let rock_match = tree.match_rocks(dep.package_req())?;
126 if !rock_match.is_found() {
127 let dep = PackageInstallSpec::new(
128 dep.package_req().clone(),
129 tree::EntryType::DependencyOnly,
130 )
131 .build_behaviour(BuildBehaviour::NoForce)
132 .pin(pin)
133 .opt(OptState::Required)
134 .maybe_source(dep.source().clone())
135 .build();
136 dependencies_to_install.push(dep);
137 }
138 }
139
140 let dependencies = Install::new(config)
141 .packages(dependencies_to_install)
142 .tree(tree.clone())
143 .install()
144 .await?;
145
146 if let Some(BuildBackendSpec::LuaRock(_)) =
147 &rockspec.build().current_platform().build_backend
148 {
149 let build_tree = tree.build_tree(config)?;
150 let luarocks = LuaRocksInstallation::new(config, build_tree)?;
151 luarocks.ensure_installed(&lua).await?;
152 }
153
154 let package = build::Build::new()
155 .rockspec(&rockspec)
156 .tree(tree)
157 .lua(&lua)
158 .entry_type(tree::EntryType::Entrypoint)
159 .config(config)
160 .pin(pin)
161 .behaviour(BuildBehaviour::Force)
162 .build()
163 .await?;
164
165 let lockfile = tree.lockfile()?;
166 let build_lockfile = tree.build_tree(config)?.lockfile()?;
167
168 let mut lockfile = lockfile.write_guard();
169 lockfile.add_entrypoint(&package);
170 for dep in dependencies {
171 lockfile.add_dependency(&package, &dep);
172 }
173 for dep in build_lockfile.rocks().values() {
174 lockfile.add_build_dependency(&package, dep);
175 }
176 Ok(package)
177 }
178}