Skip to main content

zoi_lua/
functions.rs

1//! High-level Lua environment setup and execution functions.
2//!
3//! This module provides the `setup_lua_environment` function, which is
4//! responsible for initializing the Lua state with all the necessary globals,
5//! tables, and utility functions required by Zoi package scripts.
6
7use mlua::Lua;
8use zoi_core::utils;
9
10use crate::api;
11
12/// Bootstraps the Lua environment for executing a package definition.
13///
14/// This function populates the global Lua scope with system information,
15/// directory paths, and utility functions that the `.pkg.lua` script
16/// expects to have available. It effectively creates the "sandbox" where
17/// package builds and installations are defined.
18///
19/// # Errors
20///
21/// Returns an error if the Lua environment cannot be properly initialized.
22pub fn setup_lua_environment(
23    lua: &Lua,
24    platform: &str,
25    version_override: Option<&str>,
26    file_path: Option<&str>,
27    create_pkg_dir: Option<&str>,
28    build_dir: Option<&str>,
29    staging_dir: Option<&str>,
30    sub_package: Option<&str>,
31    scope: Option<zoi_core::types::Scope>,
32    build_type: Option<&str>,
33    quiet: bool
34) -> Result<(), mlua::Error> {
35    // Initialize the global PKG table for script use
36    let pkg_table = if let Ok(table) = lua.globals().get::<mlua::Table>("PKG") {
37        table
38    } else {
39        let table = lua.create_table()?;
40        lua.globals().set("PKG", table.clone())?;
41        table
42    };
43
44    if let Some(bt) = build_type {
45        lua.globals().set("BUILD_TYPE", bt)?;
46    }
47
48    // Initialize internal metadata capture tables if they don't exist
49    if lua
50        .globals()
51        .get::<mlua::Table>("__ZoiPackageMeta")
52        .is_err()
53    {
54        let pkg_meta_table = lua.create_table()?;
55        lua.globals().set("__ZoiPackageMeta", pkg_meta_table)?;
56    }
57    if lua
58        .globals()
59        .get::<mlua::Table>("__ZoiPackageDeps")
60        .is_err()
61    {
62        let pkg_deps_table = lua.create_table()?;
63        lua.globals().set("__ZoiPackageDeps", pkg_deps_table)?;
64    }
65    if lua
66        .globals()
67        .get::<mlua::Table>("__ZoiPackageUpdates")
68        .is_err()
69    {
70        let pkg_updates_table = lua.create_table()?;
71        lua.globals()
72            .set("__ZoiPackageUpdates", pkg_updates_table)?;
73    }
74    if lua
75        .globals()
76        .get::<mlua::Table>("__ZoiPackageHooks")
77        .is_err()
78    {
79        let pkg_hooks_table = lua.create_table()?;
80        lua.globals().set("__ZoiPackageHooks", pkg_hooks_table)?;
81    }
82    if lua
83        .globals()
84        .get::<mlua::Table>("__ZoiPackageService")
85        .is_err()
86    {
87        let pkg_service_table = lua.create_table()?;
88        lua.globals()
89            .set("__ZoiPackageService", pkg_service_table)?;
90    }
91
92    // Host System Information
93    // Exposes a 'SYSTEM' table containing OS, Architecture, Distro, etc.
94    // Allow maintainers to write platform-specific logic easily.
95    let system_table = lua.create_table()?;
96    let parts: Vec<&str> = platform.split('-').collect();
97    system_table.set("OS", *parts.first().unwrap_or(&""))?;
98    system_table.set("ARCH", *parts.get(1).unwrap_or(&""))?;
99    if let Some(distro) = utils::get_linux_distribution() {
100        system_table.set("DISTRO", distro)?;
101    }
102    if let Some(de) = utils::get_desktop_environment() {
103        system_table.set("DE", de)?;
104    }
105    if let Some(server) = utils::get_display_server() {
106        system_table.set("SERVER", server)?;
107    }
108    if let Some(dv) = utils::get_distro_version() {
109        system_table.set("DISTRO_VER", dv)?;
110    }
111    if let Some(kernel) = utils::get_kernel_version() {
112        system_table.set("KERNEL_VER", kernel)?;
113    }
114    if let Some(init) = utils::get_init_system() {
115        system_table.set("INIT", init)?;
116    }
117    if let Some(cpu) = utils::get_cpu_info() {
118        system_table.set("CPU", cpu)?;
119    }
120    if let Some(gpu) = utils::get_gpu_info() {
121        system_table.set("GPU", gpu)?;
122    }
123    if let Some(manager) = utils::get_native_package_manager() {
124        system_table.set("MANAGER", manager)?;
125    }
126    lua.globals().set("SYSTEM", system_table)?;
127
128    let zoi_table = lua.create_table()?;
129    if let Some(ver) = version_override {
130        zoi_table.set("VERSION", ver)?;
131    }
132
133    if let Some(s) = scope {
134        let scope_str = format!("{s:?}").to_lowercase();
135        zoi_table.set("scope", scope_str)?;
136    }
137
138    if let Some(dir) = create_pkg_dir {
139        zoi_table.set("CREATE_PKG_DIR", dir)?;
140    }
141
142    if let Some(sub) = sub_package {
143        lua.globals().set("SUBPKG", sub)?;
144    }
145
146    let path_table = lua.create_table()?;
147    if let Ok(user_data_dir) = utils::get_user_data_dir() {
148        path_table.set("user", user_data_dir.to_string_lossy().to_string())?;
149    }
150
151    let system_bin_path = if cfg!(target_os = "windows") {
152        "C:\\ProgramData\\zoi\\pkgs\\bin".to_string()
153    } else {
154        "/usr/local/bin".to_string()
155    };
156    path_table.set("system", system_bin_path)?;
157
158    zoi_table.set("PATH", path_table)?;
159
160    if let Some(home_dir) = utils::get_user_home() {
161        pkg_table.set("home", home_dir.to_string_lossy().to_string())?;
162        if let Ok(store_dir) =
163            utils::get_store_base_dir(zoi_core::types::Scope::User)
164        {
165            pkg_table.set("store", store_dir.to_string_lossy().to_string())?;
166        }
167    }
168
169    if let Ok(current_dir) = std::env::current_dir() {
170        pkg_table.set("template", current_dir.to_string_lossy().to_string())?;
171    }
172
173    let root = if cfg!(target_os = "windows") {
174        "C:\\"
175    } else {
176        "/"
177    };
178    pkg_table.set("root", root)?;
179
180    if let Some(path_str) = file_path {
181        let abs_path = if let Ok(p) = std::fs::canonicalize(path_str) {
182            p
183        } else {
184            std::path::Path::new(path_str).to_path_buf()
185        };
186        pkg_table.set("lua", abs_path.to_string_lossy().to_string())?;
187    }
188    zoi_table.set("PKG", pkg_table.clone())?;
189
190    let location_table = lua.create_table()?;
191    if let Some(sd) = staging_dir {
192        let staging_path = std::path::Path::new(sd);
193        location_table.set(
194            "PKGSTORE",
195            staging_path.join("pkgstore").to_string_lossy().to_string()
196        )?;
197        location_table.set(
198            "HOME",
199            staging_path.join("usrhome").to_string_lossy().to_string()
200        )?;
201        location_table.set(
202            "ROOT",
203            staging_path.join("usrroot").to_string_lossy().to_string()
204        )?;
205        location_table.set(
206            "TEMPLATE",
207            staging_path
208                .join("createpkgdir")
209                .to_string_lossy()
210                .to_string()
211        )?;
212    } else {
213        if let Some(home_dir) = utils::get_user_home() {
214            location_table.set(
215                "PKGSTORE",
216                utils::get_store_base_dir(zoi_core::types::Scope::User)
217                    .map_err(|error| {
218                        mlua::Error::RuntimeError(error.to_string())
219                    })?
220                    .to_string_lossy()
221                    .to_string()
222            )?;
223            location_table
224                .set("HOME", home_dir.to_string_lossy().to_string())?;
225        }
226        let root = if cfg!(target_os = "windows") {
227            "C:\\"
228        } else {
229            "/"
230        };
231        location_table.set("ROOT", root.to_string())?;
232        if let Ok(current_dir) = std::env::current_dir() {
233            location_table
234                .set("TEMPLATE", current_dir.to_string_lossy().to_string())?;
235        }
236    }
237    if let Some(path_str) = file_path {
238        let abs_path = if let Ok(p) = std::fs::canonicalize(path_str) {
239            p
240        } else {
241            std::path::Path::new(path_str).to_path_buf()
242        };
243        location_table.set(
244            "PKGLUADIR",
245            abs_path
246                .parent()
247                .unwrap_or(&abs_path)
248                .to_string_lossy()
249                .to_string()
250        )?;
251    }
252    if let Some(bd) = build_dir {
253        location_table.set("BUILDDIR", bd)?;
254        lua.globals().set("BUILD_DIR", bd)?;
255    }
256    if let Some(sd) = staging_dir {
257        location_table.set("STAGINGDIR", sd)?;
258        lua.globals().set("STAGING_DIR", sd)?;
259    }
260    zoi_table.set("LOCATION", location_table.clone())?;
261    lua.globals().set("LOCATION", location_table)?;
262
263    lua.globals().set("ZOI", zoi_table)?;
264
265    let utils_table = lua.create_table()?;
266    lua.globals().set("UTILS", utils_table)?;
267
268    api::http::add_fetch_util(lua)?;
269    api::parse::add_parse_util(lua)?;
270    api::http::add_git_fetch_util(lua)?;
271    api::download::add_download_util(lua, quiet)?;
272    api::fs::add_file_util(lua, quiet)?;
273    api::fs::add_zcp(lua)?;
274    api::fs::add_zlicense(lua)?;
275    api::fs::add_zdoc(lua)?;
276    api::fs::add_zman(lua)?;
277    api::fs::add_zshell(lua)?;
278    api::fs::add_zsed(lua, quiet)?;
279    api::fs::add_zln(lua)?;
280    api::fs::add_zchmod(lua)?;
281    api::fs::add_zchown(lua)?;
282    api::fs::add_zmkdir(lua)?;
283    api::crypto::add_verify_hash(lua, quiet)?;
284    api::fs::add_zrm(lua)?;
285    api::system::add_cmd_util(lua, quiet)?;
286    api::system::add_zpatch(lua, quiet)?;
287    api::fs::add_fs_util(lua)?;
288    api::fs::add_find_util(lua)?;
289    api::archive::add_archive_util(lua)?;
290    api::archive::add_extract_util(lua, quiet)?;
291    api::crypto::add_verify_signature(lua, quiet)?;
292    api::crypto::add_add_pgp_key(lua, quiet)?;
293    api::lifecycle::add_package_lifecycle_functions(lua)?;
294
295    if let Some(path_str) = file_path {
296        let path = std::path::Path::new(path_str);
297        api::lifecycle::add_import_util(lua, path)?;
298        api::lifecycle::add_include_util(lua, path)?;
299    }
300
301    if let Some(sub) = sub_package {
302        lua.globals().set("SUBPKG", sub)?;
303    }
304
305    Ok(())
306}