1use std::fs;
2use std::path::Path;
3
4use mlua::{self, Lua, Table};
5use walkdir::WalkDir;
6use zoi_core::utils;
7pub fn add_file_util(lua: &Lua, quiet: bool) -> Result<(), mlua::Error> {
28 let file_fn = lua.create_function(
29 move |_, (url, path): (String, String)| -> Result<(), mlua::Error> {
30 super::download::download_with_progress(
31 &url,
32 Path::new(&path),
33 quiet
34 )
35 }
36 )?;
37
38 let utils_table: Table = lua.globals().get("UTILS")?;
39 utils_table.set("FILE", file_fn)?;
40
41 Ok(())
42}
43
44pub fn add_zcp(lua: &Lua) -> Result<(), mlua::Error> {
51 let zcp_fn =
52 lua.create_function(|lua, (source, destination): (String, String)| {
53 let ops_table: Table =
54 if let Ok(t) = lua.globals().get("__ZoiBuildOperations") {
55 t
56 } else {
57 let new_t = lua.create_table()?;
58 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
59 new_t
60 };
61 let op = lua.create_table()?;
62 op.set("op", "zcp")?;
63 op.set("source", source)?;
64 op.set("destination", destination)?;
65 ops_table.push(op)?;
66 Ok(())
67 })?;
68 lua.globals().set("zcp", zcp_fn)?;
69 Ok(())
70}
71
72pub fn add_zlicense(lua: &Lua) -> Result<(), mlua::Error> {
79 let zlicense_fn = lua.create_function(|lua, source: String| {
80 let zoi_table: Table = lua.globals().get("ZOI")?;
81 let scope: String = zoi_table
82 .get("scope")
83 .unwrap_or_else(|_| "user".to_string());
84 let pkg_table: Table = lua.globals().get("PKG")?;
85 let pkg_name: String = pkg_table
86 .get("name")
87 .unwrap_or_else(|_| "unknown".to_string());
88
89 let filename = Path::new(&source)
90 .file_name()
91 .and_then(|n| n.to_str())
92 .unwrap_or("LICENSE");
93
94 let destination = if scope == "system" {
95 format!("${{usrroot}}/usr/share/licenses/{pkg_name}/{filename}")
96 } else {
97 format!("${{pkgstore}}/{filename}")
98 };
99
100 let zcp: mlua::Function = lua.globals().get("zcp")?;
101 zcp.call::<()>((source, destination))?;
102 Ok(())
103 })?;
104 lua.globals().set("zlicense", zlicense_fn)?;
105 Ok(())
106}
107
108pub fn add_zdoc(lua: &Lua) -> Result<(), mlua::Error> {
116 let zdoc_fn = lua.create_function(|lua, source: String| {
117 let zoi_table: Table = lua.globals().get("ZOI")?;
118 let scope: String = zoi_table
119 .get("scope")
120 .unwrap_or_else(|_| "user".to_string());
121 let pkg_table: Table = lua.globals().get("PKG")?;
122 let pkg_name: String = pkg_table
123 .get("name")
124 .unwrap_or_else(|_| "unknown".to_string());
125
126 let filename = Path::new(&source)
127 .file_name()
128 .and_then(|n| n.to_str())
129 .ok_or_else(|| {
130 mlua::Error::RuntimeError("Invalid source path".to_string())
131 })?;
132
133 let destination = if scope == "system" {
134 format!("${{usrroot}}/usr/share/doc/{pkg_name}/{filename}")
135 } else {
136 format!("${{pkgstore}}/doc/{filename}")
137 };
138
139 let zcp: mlua::Function = lua.globals().get("zcp")?;
140 zcp.call::<()>((source, destination))?;
141 Ok(())
142 })?;
143 lua.globals().set("zdoc", zdoc_fn)?;
144 Ok(())
145}
146
147pub fn add_zman(lua: &Lua) -> Result<(), mlua::Error> {
154 let zman_fn = lua.create_function(
155 |lua, (source, section): (String, Option<String>)| {
156 let zoi_table: Table = lua.globals().get("ZOI")?;
157 let scope: String = zoi_table
158 .get("scope")
159 .unwrap_or_else(|_| "user".to_string());
160
161 let path = Path::new(&source);
162 let mut source_paths = Vec::new();
163
164 if path.is_dir() {
165 if let Ok(entries) = fs::read_dir(path) {
167 for entry in entries.flatten() {
168 if entry.file_type().is_ok_and(|t| t.is_file()) {
169 source_paths.push(entry.path());
170 }
171 }
172 }
173 } else {
174 source_paths.push(path.to_path_buf());
175 }
176
177 for p in source_paths {
178 let filename = p
179 .file_name()
180 .and_then(|n| n.to_str())
181 .ok_or_else(|| {
182 mlua::Error::RuntimeError(
183 "Invalid source path for zman".to_string()
184 )
185 })?;
186
187 let inferred_section = if let Some(ref s) = section {
188 s.clone()
189 } else {
190 let stem =
192 p.file_stem().and_then(|s| s.to_str()).unwrap_or("");
193 let ext =
194 p.extension().and_then(|e| e.to_str()).unwrap_or("");
195
196 if ext.parse::<u8>().is_ok() {
197 ext.to_string()
198 } else if (ext == "gz" || ext == "bz2" || ext == "xz")
199 && !stem.is_empty()
200 {
201 let inner_ext = Path::new(stem)
202 .extension()
203 .and_then(|e| e.to_str())
204 .unwrap_or("");
205 if inner_ext.parse::<u8>().is_ok() {
206 inner_ext.to_string()
207 } else {
208 "1".to_string()
209 }
210 } else {
211 "1".to_string()
212 }
213 };
214
215 let destination = if scope == "system" {
216 format!(
217 "${{usrroot}}/usr/share/man/man{inferred_section}/\
218 {filename}"
219 )
220 } else {
221 format!(
222 "${{pkgstore}}/man/man{inferred_section}/{filename}"
223 )
224 };
225
226 let zcp: mlua::Function = lua.globals().get("zcp")?;
227 zcp.call::<()>((p.to_string_lossy().to_string(), destination))?;
228 }
229 Ok(())
230 }
231 )?;
232 lua.globals().set("zman", zman_fn)?;
233 Ok(())
234}
235
236pub fn add_zshell(lua: &Lua) -> Result<(), mlua::Error> {
244 let zshell_fn =
245 lua.create_function(|lua, (source, shell): (String, String)| {
246 let filename = Path::new(&source)
247 .file_name()
248 .and_then(|n| n.to_str())
249 .ok_or_else(|| {
250 mlua::Error::RuntimeError(
251 "Invalid source path for zshell".to_string()
252 )
253 })?
254 .to_string();
255
256 let destination = format!("${{pkgstore}}/shell/{shell}/{filename}");
257 let zcp: mlua::Function = lua.globals().get("zcp")?;
258 zcp.call::<()>((source, destination.clone()))?;
259
260 let shells_table: Table =
261 if let Ok(t) = lua.globals().get("__ZoiPackageShells") {
262 t
263 } else {
264 let new_t = lua.create_table()?;
265 lua.globals().set("__ZoiPackageShells", new_t.clone())?;
266 new_t
267 };
268
269 let shell_files: Vec<String> =
270 shells_table.get(&*shell).unwrap_or_default();
271 let mut shell_files = shell_files;
272 shell_files.push(filename);
273 shells_table.set(shell, shell_files)?;
274
275 Ok(())
276 })?;
277 lua.globals().set("zshell", zshell_fn)?;
278 Ok(())
279}
280
281pub fn add_zsed(lua: &Lua, quiet: bool) -> Result<(), mlua::Error> {
289 let zsed_fn = lua.create_function(
290 move |lua, (pattern, replacement, file): (String, String, String)| {
291 let build_dir_str: String = lua.globals().get("BUILD_DIR")?;
292 let path = Path::new(&build_dir_str).join(&file);
293
294 let content = std::fs::read_to_string(&path).map_err(|e| {
295 mlua::Error::RuntimeError(format!("Failed to read {file}: {e}"))
296 })?;
297
298 let re = regex::Regex::new(&pattern).map_err(|e| {
299 mlua::Error::RuntimeError(format!(
300 "Invalid regex '{pattern}': {e}"
301 ))
302 })?;
303
304 let new_content = re.replace_all(&content, replacement.as_str());
305
306 std::fs::write(&path, new_content.as_bytes()).map_err(|e| {
307 mlua::Error::RuntimeError(format!(
308 "Failed to write {file}: {e}"
309 ))
310 })?;
311
312 if !quiet {
313 println!("Applied sed replacement to {file}");
314 }
315
316 Ok(())
317 }
318 )?;
319 lua.globals().set("zsed", zsed_fn)?;
320 Ok(())
321}
322
323pub fn add_zln(lua: &Lua) -> Result<(), mlua::Error> {
330 let zln_fn =
331 lua.create_function(|lua, (target, link): (String, String)| {
332 let ops_table: Table =
333 if let Ok(t) = lua.globals().get("__ZoiBuildOperations") {
334 t
335 } else {
336 let new_t = lua.create_table()?;
337 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
338 new_t
339 };
340 let op = lua.create_table()?;
341 op.set("op", "zln")?;
342 op.set("target", target)?;
343 op.set("link", link)?;
344 ops_table.push(op)?;
345 Ok(())
346 })?;
347 lua.globals().set("zln", zln_fn)?;
348 Ok(())
349}
350
351pub fn add_zchmod(lua: &Lua) -> Result<(), mlua::Error> {
359 let zchmod_fn =
360 lua.create_function(|lua, (path, mode): (String, u32)| {
361 let ops_table: Table =
362 if let Ok(t) = lua.globals().get("__ZoiBuildOperations") {
363 t
364 } else {
365 let new_t = lua.create_table()?;
366 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
367 new_t
368 };
369 let op = lua.create_table()?;
370 op.set("op", "zchmod")?;
371 op.set("path", path)?;
372 op.set("mode", mode)?;
373 ops_table.push(op)?;
374 Ok(())
375 })?;
376 lua.globals().set("zchmod", zchmod_fn)?;
377 Ok(())
378}
379
380pub fn add_zchown(lua: &Lua) -> Result<(), mlua::Error> {
388 let zchown_fn = lua.create_function(
389 |lua, (path, owner, group): (String, String, String)| {
390 let ops_table: Table =
391 if let Ok(t) = lua.globals().get("__ZoiBuildOperations") {
392 t
393 } else {
394 let new_t = lua.create_table()?;
395 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
396 new_t
397 };
398 let op = lua.create_table()?;
399 op.set("op", "zchown")?;
400 op.set("path", path)?;
401 op.set("owner", owner)?;
402 op.set("group", group)?;
403 ops_table.push(op)?;
404 Ok(())
405 }
406 )?;
407 lua.globals().set("zchown", zchown_fn)?;
408 Ok(())
409}
410
411pub fn add_zmkdir(lua: &Lua) -> Result<(), mlua::Error> {
418 let zmkdir_fn = lua.create_function(|lua, path: String| {
419 let ops_table: Table =
420 if let Ok(t) = lua.globals().get("__ZoiBuildOperations") {
421 t
422 } else {
423 let new_t = lua.create_table()?;
424 lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
425 new_t
426 };
427 let op = lua.create_table()?;
428 op.set("op", "zmkdir")?;
429 op.set("path", path)?;
430 ops_table.push(op)?;
431 Ok(())
432 })?;
433 lua.globals().set("zmkdir", zmkdir_fn)?;
434 Ok(())
435}
436
437pub fn add_zrm(lua: &Lua) -> Result<(), mlua::Error> {
445 let zrm_fn = lua.create_function(|lua, path: String| {
446 let ops_table: Table =
447 if let Ok(t) = lua.globals().get("__ZoiUninstallOperations") {
448 t
449 } else {
450 let new_t = lua.create_table()?;
451 lua.globals()
452 .set("__ZoiUninstallOperations", new_t.clone())?;
453 new_t
454 };
455 let op = lua.create_table()?;
456 op.set("op", "zrm")?;
457 op.set("path", path)?;
458 ops_table.push(op)?;
459 Ok(())
460 })?;
461 lua.globals().set("zrm", zrm_fn)?;
462 Ok(())
463}
464
465pub fn add_fs_util(lua: &Lua) -> Result<(), mlua::Error> {
472 let fs_table = lua.create_table()?;
473
474 let exists_fn = lua.create_function(|lua, path: String| {
475 let p = Path::new(&path);
476 if p.exists() {
477 return Ok(true);
478 }
479 if let Ok(build_dir) = lua.globals().get::<String>("BUILD_DIR")
480 && Path::new(&build_dir).join(p).exists()
481 {
482 return Ok(true);
483 }
484 Ok(false)
485 })?;
486 fs_table.set("exists", exists_fn)?;
487
488 let copy_fn = lua.create_function(|_, (src, dest): (String, String)| {
489 let src_path = Path::new(&src);
490 let dest_path = Path::new(&dest);
491 if src_path.is_dir() {
492 utils::copy_dir_all(src_path, dest_path)
493 .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
494 } else {
495 fs::copy(src_path, dest_path)
496 .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
497 }
498 Ok(true)
499 })?;
500 fs_table.set("copy", copy_fn)?;
501
502 let move_fn = lua.create_function(|_, (src, dest): (String, String)| {
503 fs::rename(src, dest)
504 .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
505 Ok(true)
506 })?;
507 fs_table.set("move", move_fn)?;
508
509 let chmod_fn = lua.create_function(|_, (path, mode): (String, u32)| {
510 #[cfg(unix)]
511 {
512 use std::os::unix::fs::PermissionsExt;
513 fs::set_permissions(path, fs::Permissions::from_mode(mode))
514 .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
515 }
516 #[cfg(windows)]
517 {
518 let _ = (path, mode);
519 }
520 Ok(true)
521 })?;
522 fs_table.set("chmod", chmod_fn)?;
523
524 let utils_table: Table = lua.globals().get("UTILS")?;
525 utils_table.set("FS", fs_table)?;
526
527 Ok(())
528}
529
530pub fn add_find_util(lua: &Lua) -> Result<(), mlua::Error> {
537 let find_table = lua.create_table()?;
538
539 let find_file_fn =
540 lua.create_function(|lua, (dir, name): (String, String)| {
541 let build_dir_str: String = lua.globals().get("BUILD_DIR")?;
542 let search_dir = Path::new(&build_dir_str).join(dir);
543 for entry in WalkDir::new(search_dir) {
544 let entry = entry
545 .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
546 if entry.file_name().to_string_lossy() == name {
547 let path = entry.path();
548 let relative_path = path
549 .strip_prefix(Path::new(&build_dir_str))
550 .map_err(|e| {
551 mlua::Error::RuntimeError(format!(
552 "Failed to determine relative path for {}: {e}",
553 path.display()
554 ))
555 })?;
556 return Ok(Some(
557 relative_path.to_string_lossy().to_string()
558 ));
559 }
560 }
561 Ok(None)
562 })?;
563 find_table.set("file", find_file_fn)?;
564
565 let utils_table: Table = lua.globals().get("UTILS")?;
566 utils_table.set("FIND", find_table)?;
567
568 Ok(())
569}