1use mlua::{self, Lua, Table};
2use std::path::Path;
3use zoi_core::utils;
4
5use std::fs;
6use walkdir::WalkDir;
7pub fn add_file_util(lua: &Lua, quiet: bool) -> Result<(), mlua::Error> {
19 let file_fn = lua.create_function(
20 move |_, (url, path): (String, String)| -> Result<(), mlua::Error> {
21 super::download::download_with_progress(&url, Path::new(&path), quiet)
22 },
23 )?;
24
25 let utils_table: Table = lua.globals().get("UTILS")?;
26 utils_table.set("FILE", file_fn)?;
27
28 Ok(())
29}
30
31pub fn add_zcp(lua: &Lua) -> Result<(), mlua::Error> {
32 let zcp_fn = lua.create_function(|lua, (source, destination): (String, String)| {
33 let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
34 Ok(t) => t,
35 Err(_) => {
36 let new_t = lua.create_table()?;
37 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
38 new_t
39 }
40 };
41 let op = lua.create_table()?;
42 op.set("op", "zcp")?;
43 op.set("source", source)?;
44 op.set("destination", destination)?;
45 ops_table.push(op)?;
46 Ok(())
47 })?;
48 lua.globals().set("zcp", zcp_fn)?;
49 Ok(())
50}
51
52pub fn add_zlicense(lua: &Lua) -> Result<(), mlua::Error> {
53 let zlicense_fn = lua.create_function(|lua, source: String| {
54 let zoi_table: Table = lua.globals().get("ZOI")?;
55 let scope: String = zoi_table
56 .get("scope")
57 .unwrap_or_else(|_| "user".to_string());
58 let pkg_table: Table = lua.globals().get("PKG")?;
59 let pkg_name: String = pkg_table
60 .get("name")
61 .unwrap_or_else(|_| "unknown".to_string());
62
63 let filename = Path::new(&source)
64 .file_name()
65 .and_then(|n| n.to_str())
66 .unwrap_or("LICENSE");
67
68 let destination = if scope == "system" {
69 format!("${{usrroot}}/usr/share/licenses/{}/{}", pkg_name, filename)
70 } else {
71 format!("${{pkgstore}}/{}", filename)
72 };
73
74 let zcp: mlua::Function = lua.globals().get("zcp")?;
75 zcp.call::<()>((source, destination))?;
76 Ok(())
77 })?;
78 lua.globals().set("zlicense", zlicense_fn)?;
79 Ok(())
80}
81
82pub fn add_zdoc(lua: &Lua) -> Result<(), mlua::Error> {
83 let zdoc_fn = lua.create_function(|lua, source: String| {
84 let zoi_table: Table = lua.globals().get("ZOI")?;
85 let scope: String = zoi_table
86 .get("scope")
87 .unwrap_or_else(|_| "user".to_string());
88 let pkg_table: Table = lua.globals().get("PKG")?;
89 let pkg_name: String = pkg_table
90 .get("name")
91 .unwrap_or_else(|_| "unknown".to_string());
92
93 let filename = Path::new(&source)
94 .file_name()
95 .and_then(|n| n.to_str())
96 .ok_or_else(|| mlua::Error::RuntimeError("Invalid source path".to_string()))?;
97
98 let destination = if scope == "system" {
99 format!("${{usrroot}}/usr/share/doc/{}/{}", pkg_name, filename)
100 } else {
101 format!("${{pkgstore}}/doc/{}", filename)
102 };
103
104 let zcp: mlua::Function = lua.globals().get("zcp")?;
105 zcp.call::<()>((source, destination))?;
106 Ok(())
107 })?;
108 lua.globals().set("zdoc", zdoc_fn)?;
109 Ok(())
110}
111
112pub fn add_zman(lua: &Lua) -> Result<(), mlua::Error> {
113 let zman_fn = lua.create_function(|lua, (source, section): (String, Option<String>)| {
114 let zoi_table: Table = lua.globals().get("ZOI")?;
115 let scope: String = zoi_table
116 .get("scope")
117 .unwrap_or_else(|_| "user".to_string());
118
119 let path = Path::new(&source);
120 let filename = path
121 .file_name()
122 .and_then(|n| n.to_str())
123 .ok_or_else(|| mlua::Error::RuntimeError("Invalid source path for zman".to_string()))?;
124
125 let inferred_section = if let Some(s) = section {
126 s
127 } else {
128 let stem = path.file_stem().and_then(|s| s.to_str()).unwrap_or("");
130 let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
131
132 if ext.parse::<u8>().is_ok() {
133 ext.to_string()
134 } else if (ext == "gz" || ext == "bz2" || ext == "xz") && !stem.is_empty() {
135 let inner_ext = Path::new(stem)
136 .extension()
137 .and_then(|e| e.to_str())
138 .unwrap_or("");
139 if inner_ext.parse::<u8>().is_ok() {
140 inner_ext.to_string()
141 } else {
142 "1".to_string()
143 }
144 } else {
145 "1".to_string()
146 }
147 };
148
149 let destination = if scope == "system" {
150 format!(
151 "${{usrroot}}/usr/share/man/man{}/{}",
152 inferred_section, filename
153 )
154 } else {
155 format!("${{pkgstore}}/man/man{}/{}", inferred_section, filename)
156 };
157
158 let zcp: mlua::Function = lua.globals().get("zcp")?;
159 zcp.call::<()>((source, destination))?;
160 Ok(())
161 })?;
162 lua.globals().set("zman", zman_fn)?;
163 Ok(())
164}
165
166pub fn add_zshell(lua: &Lua) -> Result<(), mlua::Error> {
167 let zshell_fn = lua.create_function(|lua, (source, shell): (String, String)| {
168 let filename = Path::new(&source)
169 .file_name()
170 .and_then(|n| n.to_str())
171 .ok_or_else(|| mlua::Error::RuntimeError("Invalid source path for zshell".to_string()))?
172 .to_string();
173
174 let destination = format!("${{pkgstore}}/shell/{}/{}", shell, filename);
175 let zcp: mlua::Function = lua.globals().get("zcp")?;
176 zcp.call::<()>((source, destination.clone()))?;
177
178 let shells_table: Table = match lua.globals().get("__ZoiPackageShells") {
179 Ok(t) => t,
180 Err(_) => {
181 let new_t = lua.create_table()?;
182 lua.globals().set("__ZoiPackageShells", new_t.clone())?;
183 new_t
184 }
185 };
186
187 let shell_files: Vec<String> = shells_table.get(&*shell).unwrap_or_default();
188 let mut shell_files = shell_files;
189 shell_files.push(filename);
190 shells_table.set(shell, shell_files)?;
191
192 Ok(())
193 })?;
194 lua.globals().set("zshell", zshell_fn)?;
195 Ok(())
196}
197
198pub fn add_zsed(lua: &Lua, quiet: bool) -> Result<(), mlua::Error> {
199 let zsed_fn = lua.create_function(
200 move |lua, (pattern, replacement, file): (String, String, String)| {
201 let build_dir_str: String = lua.globals().get("BUILD_DIR")?;
202 let path = Path::new(&build_dir_str).join(&file);
203
204 let content = std::fs::read_to_string(&path).map_err(|e| {
205 mlua::Error::RuntimeError(format!("Failed to read {}: {}", file, e))
206 })?;
207
208 let re = regex::Regex::new(&pattern).map_err(|e| {
209 mlua::Error::RuntimeError(format!("Invalid regex '{}': {}", pattern, e))
210 })?;
211
212 let new_content = re.replace_all(&content, replacement.as_str());
213
214 std::fs::write(&path, new_content.as_bytes()).map_err(|e| {
215 mlua::Error::RuntimeError(format!("Failed to write {}: {}", file, e))
216 })?;
217
218 if !quiet {
219 println!("Applied sed replacement to {}", file);
220 }
221
222 Ok(())
223 },
224 )?;
225 lua.globals().set("zsed", zsed_fn)?;
226 Ok(())
227}
228
229pub fn add_zln(lua: &Lua) -> Result<(), mlua::Error> {
230 let zln_fn = lua.create_function(|lua, (target, link): (String, String)| {
231 let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
232 Ok(t) => t,
233 Err(_) => {
234 let new_t = lua.create_table()?;
235 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
236 new_t
237 }
238 };
239 let op = lua.create_table()?;
240 op.set("op", "zln")?;
241 op.set("target", target)?;
242 op.set("link", link)?;
243 ops_table.push(op)?;
244 Ok(())
245 })?;
246 lua.globals().set("zln", zln_fn)?;
247 Ok(())
248}
249
250pub fn add_zchmod(lua: &Lua) -> Result<(), mlua::Error> {
251 let zchmod_fn = lua.create_function(|lua, (path, mode): (String, u32)| {
252 let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
253 Ok(t) => t,
254 Err(_) => {
255 let new_t = lua.create_table()?;
256 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
257 new_t
258 }
259 };
260 let op = lua.create_table()?;
261 op.set("op", "zchmod")?;
262 op.set("path", path)?;
263 op.set("mode", mode)?;
264 ops_table.push(op)?;
265 Ok(())
266 })?;
267 lua.globals().set("zchmod", zchmod_fn)?;
268 Ok(())
269}
270
271pub fn add_zchown(lua: &Lua) -> Result<(), mlua::Error> {
272 let zchown_fn =
273 lua.create_function(|lua, (path, owner, group): (String, String, String)| {
274 let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
275 Ok(t) => t,
276 Err(_) => {
277 let new_t = lua.create_table()?;
278 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
279 new_t
280 }
281 };
282 let op = lua.create_table()?;
283 op.set("op", "zchown")?;
284 op.set("path", path)?;
285 op.set("owner", owner)?;
286 op.set("group", group)?;
287 ops_table.push(op)?;
288 Ok(())
289 })?;
290 lua.globals().set("zchown", zchown_fn)?;
291 Ok(())
292}
293
294pub fn add_zmkdir(lua: &Lua) -> Result<(), mlua::Error> {
295 let zmkdir_fn = lua.create_function(|lua, path: String| {
296 let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
297 Ok(t) => t,
298 Err(_) => {
299 let new_t = lua.create_table()?;
300 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
301 new_t
302 }
303 };
304 let op = lua.create_table()?;
305 op.set("op", "zmkdir")?;
306 op.set("path", path)?;
307 ops_table.push(op)?;
308 Ok(())
309 })?;
310 lua.globals().set("zmkdir", zmkdir_fn)?;
311 Ok(())
312}
313
314pub fn add_zrm(lua: &Lua) -> Result<(), mlua::Error> {
315 let zrm_fn = lua.create_function(|lua, path: String| {
316 let ops_table: Table = match lua.globals().get("__ZoiUninstallOperations") {
317 Ok(t) => t,
318 Err(_) => {
319 let new_t = lua.create_table()?;
320 lua.globals()
321 .set("__ZoiUninstallOperations", new_t.clone())?;
322 new_t
323 }
324 };
325 let op = lua.create_table()?;
326 op.set("op", "zrm")?;
327 op.set("path", path)?;
328 ops_table.push(op)?;
329 Ok(())
330 })?;
331 lua.globals().set("zrm", zrm_fn)?;
332 Ok(())
333}
334
335pub fn add_fs_util(lua: &Lua) -> Result<(), mlua::Error> {
336 let fs_table = lua.create_table()?;
337
338 let exists_fn = lua.create_function(|lua, path: String| {
339 let p = Path::new(&path);
340 if p.exists() {
341 return Ok(true);
342 }
343 if let Ok(build_dir) = lua.globals().get::<String>("BUILD_DIR")
344 && Path::new(&build_dir).join(p).exists()
345 {
346 return Ok(true);
347 }
348 Ok(false)
349 })?;
350 fs_table.set("exists", exists_fn)?;
351
352 let copy_fn = lua.create_function(|_, (src, dest): (String, String)| {
353 let src_path = Path::new(&src);
354 let dest_path = Path::new(&dest);
355 if src_path.is_dir() {
356 utils::copy_dir_all(src_path, dest_path)
357 .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
358 } else {
359 fs::copy(src_path, dest_path).map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
360 }
361 Ok(true)
362 })?;
363 fs_table.set("copy", copy_fn)?;
364
365 let move_fn = lua.create_function(|_, (src, dest): (String, String)| {
366 fs::rename(src, dest).map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
367 Ok(true)
368 })?;
369 fs_table.set("move", move_fn)?;
370
371 let chmod_fn = lua.create_function(|_, (path, mode): (String, u32)| {
372 #[cfg(unix)]
373 {
374 use std::os::unix::fs::PermissionsExt;
375 fs::set_permissions(path, fs::Permissions::from_mode(mode))
376 .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
377 }
378 #[cfg(windows)]
379 {
380 let _ = (path, mode);
381 }
382 Ok(true)
383 })?;
384 fs_table.set("chmod", chmod_fn)?;
385
386 let utils_table: Table = lua.globals().get("UTILS")?;
387 utils_table.set("FS", fs_table)?;
388
389 Ok(())
390}
391
392pub fn add_find_util(lua: &Lua) -> Result<(), mlua::Error> {
393 let find_table = lua.create_table()?;
394
395 let find_file_fn = lua.create_function(|lua, (dir, name): (String, String)| {
396 let build_dir_str: String = lua.globals().get("BUILD_DIR")?;
397 let search_dir = Path::new(&build_dir_str).join(dir);
398 for entry in WalkDir::new(search_dir) {
399 let entry = entry.map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
400 if entry.file_name().to_string_lossy() == name {
401 let path = entry.path();
402 let relative_path = path.strip_prefix(Path::new(&build_dir_str)).map_err(|e| {
403 mlua::Error::RuntimeError(format!(
404 "Failed to determine relative path for {:?}: {}",
405 path, e
406 ))
407 })?;
408 return Ok(Some(relative_path.to_string_lossy().to_string()));
409 }
410 }
411 Ok(None)
412 })?;
413 find_table.set("file", find_file_fn)?;
414
415 let utils_table: Table = lua.globals().get("UTILS")?;
416 utils_table.set("FIND", find_table)?;
417
418 Ok(())
419}