Skip to main content

zoi_lua/api/
lifecycle.rs

1use mlua::{self, Lua, LuaSerdeExt, Table, Value};
2use std::fs;
3use std::path::Path;
4
5/// Exposes the core Package DSL and lifecycle functions to the Lua environment.
6///
7/// This module defines the primary entry points for a `.pkg.lua` script:
8/// - `metadata`: Defines the static `Package` struct fields.
9/// - `dependencies`: Defines the runtime and build dependency graph.
10/// - `prepare`/`build`/`package`: Placeholder functions that the maintainer overrides
11///   to define the build logic.
12/// - `IMPORT`/`INCLUDE`: Helpers for modular package definitions.
13///
14/// These functions bridge the declarative metadata and the imperative build logic.
15pub fn add_import_util(lua: &Lua, current_path: &Path) -> Result<(), mlua::Error> {
16    let current_path_buf = current_path.to_path_buf();
17    let import_fn = lua.create_function(move |lua, file_name: String| {
18        if let Ok(refs) = lua.globals().get::<Table>("__ZoiReferencedFiles") {
19            let _ = refs.push(file_name.clone());
20        }
21        let parent = current_path_buf.parent().ok_or_else(|| {
22            mlua::Error::RuntimeError(
23                "Could not determine parent directory of package file".to_string(),
24            )
25        })?;
26        let path = parent.join(&file_name);
27        let content =
28            fs::read_to_string(&path).map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
29
30        if let Some(extension) = path.extension().and_then(|s| s.to_str()) {
31            match extension {
32                "json" => {
33                    let value: serde_json::Value = serde_json::from_str(&content)
34                        .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
35                    return lua.to_value(&value);
36                }
37                "yaml" | "yml" => {
38                    let value: serde_yaml::Value = serde_yaml::from_str(&content)
39                        .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
40                    return lua.to_value(&value);
41                }
42                "toml" => {
43                    let value: toml::Value = toml::from_str(&content)
44                        .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
45                    return lua.to_value(&value);
46                }
47                _ => {
48                    return lua.to_value(&content);
49                }
50            }
51        }
52
53        lua.to_value(&content)
54    })?;
55    lua.globals().set("IMPORT", import_fn)?;
56    Ok(())
57}
58
59pub fn add_include_util(lua: &Lua, current_path: &Path) -> Result<(), mlua::Error> {
60    let current_path_buf = current_path.to_path_buf();
61    let include_fn =
62        lua.create_function(move |lua, file_name: String| -> Result<(), mlua::Error> {
63            if let Ok(refs) = lua.globals().get::<Table>("__ZoiReferencedFiles") {
64                let _ = refs.push(file_name.clone());
65            }
66            let parent = current_path_buf.parent().ok_or_else(|| {
67                mlua::Error::RuntimeError(
68                    "Could not determine parent directory of package file".to_string(),
69                )
70            })?;
71            let path = parent.join(file_name);
72            let code =
73                fs::read_to_string(path).map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
74            lua.load(&code).exec()?;
75            Ok(())
76        })?;
77    lua.globals().set("INCLUDE", include_fn)?;
78    Ok(())
79}
80
81pub fn add_package_lifecycle_functions(lua: &Lua) -> Result<(), mlua::Error> {
82    let metadata_fn = lua.create_function(move |lua, pkg_def: Table| {
83        if let Ok(meta_table) = lua.globals().get::<Table>("__ZoiPackageMeta")
84            && let Ok(pkg_global) = lua.globals().get::<Table>("PKG")
85        {
86            for pair in pkg_def.pairs::<Value, Value>() {
87                let (key, value) = pair?;
88                meta_table.set(key.clone(), value.clone())?;
89                pkg_global.set(key, value)?;
90            }
91        }
92        Ok(())
93    })?;
94    lua.globals().set("metadata", metadata_fn)?;
95
96    let dependencies_fn = lua.create_function(move |lua, deps_def: Table| {
97        if let Ok(deps_table) = lua.globals().get::<Table>("__ZoiPackageDeps") {
98            for pair in deps_def.pairs::<String, Value>() {
99                let (key, value) = pair?;
100                deps_table.set(key, value)?;
101            }
102        }
103        Ok(())
104    })?;
105    lua.globals().set("dependencies", dependencies_fn)?;
106
107    let updates_fn = lua.create_function(move |lua, updates_list: Table| {
108        if let Ok(updates_table) = lua.globals().get::<Table>("__ZoiPackageUpdates") {
109            for pair in updates_list.pairs::<Value, Table>() {
110                let (_, update_info) = pair?;
111                updates_table.push(update_info)?;
112            }
113        }
114        Ok(())
115    })?;
116    lua.globals().set("updates", updates_fn)?;
117
118    let hooks_fn = lua.create_function(move |lua, hooks_def: Table| {
119        if let Ok(hooks_table) = lua.globals().get::<Table>("__ZoiPackageHooks") {
120            for pair in hooks_def.pairs::<String, Value>() {
121                let (key, value) = pair?;
122                hooks_table.set(key, value)?;
123            }
124        }
125        Ok(())
126    })?;
127    lua.globals().set("hooks", hooks_fn)?;
128
129    let service_fn = lua.create_function(move |lua, service_def: Table| {
130        if let Ok(service_table) = lua.globals().get::<Table>("__ZoiPackageService") {
131            for pair in service_def.pairs::<String, Value>() {
132                let (key, value) = pair?;
133                service_table.set(key, value)?;
134            }
135        }
136        Ok(())
137    })?;
138    lua.globals().set("service", service_fn)?;
139
140    let prepare_fn = lua.create_function(|_, _: mlua::MultiValue| Ok(()))?;
141    lua.globals().set("prepare", prepare_fn)?;
142    let package_fn = lua.create_function(|_, _: mlua::MultiValue| Ok(()))?;
143    lua.globals().set("package", package_fn)?;
144    let verify_fn = lua.create_function(|_, _: mlua::MultiValue| Ok(true))?;
145    lua.globals().set("verify", verify_fn)?;
146    let test_fn = lua.create_function(|_, _: mlua::MultiValue| Ok(true))?;
147    lua.globals().set("test", test_fn)?;
148    let uninstall_fn = lua.create_function(|_, _: mlua::MultiValue| Ok(()))?;
149    lua.globals().set("uninstall", uninstall_fn)?;
150
151    Ok(())
152}