Skip to main content

zoi_lua/api/
fs.rs

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) -> Result<(), mlua::Error> {
8    let file_fn = lua.create_function(
9        |_, (url, path): (String, String)| -> Result<(), mlua::Error> {
10            let client =
11                utils::get_http_client().map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
12            let mut attempt = 0u32;
13            let response = loop {
14                attempt += 1;
15                match client.get(&url).send() {
16                    Ok(resp) => break resp,
17                    Err(e) => {
18                        if attempt < 3 {
19                            eprintln!("Download failed ({}). Retrying...", e);
20                            zoi_core::utils::retry_backoff_sleep(attempt);
21                            continue;
22                        } else {
23                            return Err(mlua::Error::RuntimeError(e.to_string()));
24                        }
25                    }
26                }
27            };
28            let content = response
29                .bytes()
30                .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
31            fs::write(path, content).map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
32            Ok(())
33        },
34    )?;
35
36    let utils_table: Table = lua.globals().get("UTILS")?;
37    utils_table.set("FILE", file_fn)?;
38
39    Ok(())
40}
41
42pub fn add_zcp(lua: &Lua) -> Result<(), mlua::Error> {
43    let zcp_fn = lua.create_function(|lua, (source, destination): (String, String)| {
44        let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
45            Ok(t) => t,
46            Err(_) => {
47                let new_t = lua.create_table()?;
48                lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
49                new_t
50            }
51        };
52        let op = lua.create_table()?;
53        op.set("op", "zcp")?;
54        op.set("source", source)?;
55        op.set("destination", destination)?;
56        ops_table.push(op)?;
57        Ok(())
58    })?;
59    lua.globals().set("zcp", zcp_fn)?;
60    Ok(())
61}
62
63pub fn add_zlicense(lua: &Lua) -> Result<(), mlua::Error> {
64    let zlicense_fn = lua.create_function(|lua, source: String| {
65        let destination = "${pkgstore}/LICENSE".to_string();
66        let zcp: mlua::Function = lua.globals().get("zcp")?;
67        zcp.call::<()>((source, destination))?;
68        Ok(())
69    })?;
70    lua.globals().set("zlicense", zlicense_fn)?;
71    Ok(())
72}
73
74pub fn add_zdoc(lua: &Lua) -> Result<(), mlua::Error> {
75    let zdoc_fn = lua.create_function(|lua, source: String| {
76        let filename = Path::new(&source)
77            .file_name()
78            .and_then(|n| n.to_str())
79            .ok_or_else(|| mlua::Error::RuntimeError("Invalid source path".to_string()))?;
80        let destination = format!("${{pkgstore}}/doc/{}", filename);
81        let zcp: mlua::Function = lua.globals().get("zcp")?;
82        zcp.call::<()>((source, destination))?;
83        Ok(())
84    })?;
85    lua.globals().set("zdoc", zdoc_fn)?;
86    Ok(())
87}
88
89pub fn add_zshell(lua: &Lua) -> Result<(), mlua::Error> {
90    let zshell_fn = lua.create_function(|lua, (source, shell): (String, String)| {
91        let filename = Path::new(&source)
92            .file_name()
93            .and_then(|n| n.to_str())
94            .ok_or_else(|| mlua::Error::RuntimeError("Invalid source path for zshell".to_string()))?
95            .to_string();
96
97        let destination = format!("${{pkgstore}}/shell/{}/{}", shell, filename);
98        let zcp: mlua::Function = lua.globals().get("zcp")?;
99        zcp.call::<()>((source, destination.clone()))?;
100
101        let shells_table: Table = match lua.globals().get("__ZoiPackageShells") {
102            Ok(t) => t,
103            Err(_) => {
104                let new_t = lua.create_table()?;
105                lua.globals().set("__ZoiPackageShells", new_t.clone())?;
106                new_t
107            }
108        };
109
110        let shell_files: Vec<String> = shells_table.get(&*shell).unwrap_or_default();
111        let mut shell_files = shell_files;
112        shell_files.push(filename);
113        shells_table.set(shell, shell_files)?;
114
115        Ok(())
116    })?;
117    lua.globals().set("zshell", zshell_fn)?;
118    Ok(())
119}
120
121pub fn add_zsed(lua: &Lua, quiet: bool) -> Result<(), mlua::Error> {
122    let zsed_fn = lua.create_function(
123        move |lua, (pattern, replacement, file): (String, String, String)| {
124            let build_dir_str: String = lua.globals().get("BUILD_DIR")?;
125            let path = Path::new(&build_dir_str).join(&file);
126
127            let content = std::fs::read_to_string(&path).map_err(|e| {
128                mlua::Error::RuntimeError(format!("Failed to read {}: {}", file, e))
129            })?;
130
131            let re = regex::Regex::new(&pattern).map_err(|e| {
132                mlua::Error::RuntimeError(format!("Invalid regex '{}': {}", pattern, e))
133            })?;
134
135            let new_content = re.replace_all(&content, replacement.as_str());
136
137            std::fs::write(&path, new_content.as_bytes()).map_err(|e| {
138                mlua::Error::RuntimeError(format!("Failed to write {}: {}", file, e))
139            })?;
140
141            if !quiet {
142                println!("Applied sed replacement to {}", file);
143            }
144
145            Ok(())
146        },
147    )?;
148    lua.globals().set("zsed", zsed_fn)?;
149    Ok(())
150}
151
152pub fn add_zln(lua: &Lua) -> Result<(), mlua::Error> {
153    let zln_fn = lua.create_function(|lua, (target, link): (String, String)| {
154        let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
155            Ok(t) => t,
156            Err(_) => {
157                let new_t = lua.create_table()?;
158                lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
159                new_t
160            }
161        };
162        let op = lua.create_table()?;
163        op.set("op", "zln")?;
164        op.set("target", target)?;
165        op.set("link", link)?;
166        ops_table.push(op)?;
167        Ok(())
168    })?;
169    lua.globals().set("zln", zln_fn)?;
170    Ok(())
171}
172
173pub fn add_zchmod(lua: &Lua) -> Result<(), mlua::Error> {
174    let zchmod_fn = lua.create_function(|lua, (path, mode): (String, u32)| {
175        let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
176            Ok(t) => t,
177            Err(_) => {
178                let new_t = lua.create_table()?;
179                lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
180                new_t
181            }
182        };
183        let op = lua.create_table()?;
184        op.set("op", "zchmod")?;
185        op.set("path", path)?;
186        op.set("mode", mode)?;
187        ops_table.push(op)?;
188        Ok(())
189    })?;
190    lua.globals().set("zchmod", zchmod_fn)?;
191    Ok(())
192}
193
194pub fn add_zchown(lua: &Lua) -> Result<(), mlua::Error> {
195    let zchown_fn =
196        lua.create_function(|lua, (path, owner, group): (String, String, String)| {
197            let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
198                Ok(t) => t,
199                Err(_) => {
200                    let new_t = lua.create_table()?;
201                    lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
202                    new_t
203                }
204            };
205            let op = lua.create_table()?;
206            op.set("op", "zchown")?;
207            op.set("path", path)?;
208            op.set("owner", owner)?;
209            op.set("group", group)?;
210            ops_table.push(op)?;
211            Ok(())
212        })?;
213    lua.globals().set("zchown", zchown_fn)?;
214    Ok(())
215}
216
217pub fn add_zmkdir(lua: &Lua) -> Result<(), mlua::Error> {
218    let zmkdir_fn = lua.create_function(|lua, path: String| {
219        let ops_table: Table = match lua.globals().get("__ZoiBuildOperations") {
220            Ok(t) => t,
221            Err(_) => {
222                let new_t = lua.create_table()?;
223                lua.globals().set("__ZoiBuildOperations", new_t.clone())?;
224                new_t
225            }
226        };
227        let op = lua.create_table()?;
228        op.set("op", "zmkdir")?;
229        op.set("path", path)?;
230        ops_table.push(op)?;
231        Ok(())
232    })?;
233    lua.globals().set("zmkdir", zmkdir_fn)?;
234    Ok(())
235}
236
237pub fn add_zrm(lua: &Lua) -> Result<(), mlua::Error> {
238    let zrm_fn = lua.create_function(|lua, path: String| {
239        let ops_table: Table = match lua.globals().get("__ZoiUninstallOperations") {
240            Ok(t) => t,
241            Err(_) => {
242                let new_t = lua.create_table()?;
243                lua.globals()
244                    .set("__ZoiUninstallOperations", new_t.clone())?;
245                new_t
246            }
247        };
248        let op = lua.create_table()?;
249        op.set("op", "zrm")?;
250        op.set("path", path)?;
251        ops_table.push(op)?;
252        Ok(())
253    })?;
254    lua.globals().set("zrm", zrm_fn)?;
255    Ok(())
256}
257
258pub fn add_fs_util(lua: &Lua) -> Result<(), mlua::Error> {
259    let fs_table = lua.create_table()?;
260
261    let exists_fn = lua.create_function(|lua, path: String| {
262        let p = Path::new(&path);
263        if p.exists() {
264            return Ok(true);
265        }
266        if let Ok(build_dir) = lua.globals().get::<String>("BUILD_DIR")
267            && Path::new(&build_dir).join(p).exists()
268        {
269            return Ok(true);
270        }
271        Ok(false)
272    })?;
273    fs_table.set("exists", exists_fn)?;
274
275    let copy_fn = lua.create_function(|_, (src, dest): (String, String)| {
276        let src_path = Path::new(&src);
277        let dest_path = Path::new(&dest);
278        if src_path.is_dir() {
279            utils::copy_dir_all(src_path, dest_path)
280                .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
281        } else {
282            fs::copy(src_path, dest_path).map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
283        }
284        Ok(true)
285    })?;
286    fs_table.set("copy", copy_fn)?;
287
288    let move_fn = lua.create_function(|_, (src, dest): (String, String)| {
289        fs::rename(src, dest).map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
290        Ok(true)
291    })?;
292    fs_table.set("move", move_fn)?;
293
294    let chmod_fn = lua.create_function(|_, (path, mode): (String, u32)| {
295        #[cfg(unix)]
296        {
297            use std::os::unix::fs::PermissionsExt;
298            fs::set_permissions(path, fs::Permissions::from_mode(mode))
299                .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
300        }
301        #[cfg(windows)]
302        {
303            let _ = (path, mode);
304        }
305        Ok(true)
306    })?;
307    fs_table.set("chmod", chmod_fn)?;
308
309    let utils_table: Table = lua.globals().get("UTILS")?;
310    utils_table.set("FS", fs_table)?;
311
312    Ok(())
313}
314
315pub fn add_find_util(lua: &Lua) -> Result<(), mlua::Error> {
316    let find_table = lua.create_table()?;
317
318    let find_file_fn = lua.create_function(|lua, (dir, name): (String, String)| {
319        let build_dir_str: String = lua.globals().get("BUILD_DIR")?;
320        let search_dir = Path::new(&build_dir_str).join(dir);
321        for entry in WalkDir::new(search_dir) {
322            let entry = entry.map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
323            if entry.file_name().to_string_lossy() == name {
324                let path = entry.path();
325                let relative_path = path.strip_prefix(Path::new(&build_dir_str)).map_err(|e| {
326                    mlua::Error::RuntimeError(format!(
327                        "Failed to determine relative path for {:?}: {}",
328                        path, e
329                    ))
330                })?;
331                return Ok(Some(relative_path.to_string_lossy().to_string()));
332            }
333        }
334        Ok(None)
335    })?;
336    find_table.set("file", find_file_fn)?;
337
338    let utils_table: Table = lua.globals().get("UTILS")?;
339    utils_table.set("FIND", find_table)?;
340
341    Ok(())
342}