1use crate::context::with_context;
6use crate::error::Result;
7use std::fs;
8use std::path::PathBuf;
9use steel::SteelVal;
10use steel::steel_vm::engine::Engine;
11use steel::steel_vm::register_fn::RegisterFn;
12
13pub fn register(engine: &mut Engine) -> Result<()> {
15 engine.register_fn("hx/read-file", read_file);
16 engine.register_fn("hx/write-file", write_file);
17 engine.register_fn("hx/file-exists?", file_exists);
18 engine.register_fn("hx/glob", glob_files);
19 engine.register_fn("hx/path-join", path_join);
20 engine.register_fn("hx/mkdir", mkdir);
21 Ok(())
22}
23
24fn read_file(path: String) -> std::result::Result<SteelVal, String> {
26 let resolved_path = resolve_path(&path);
27
28 fs::read_to_string(&resolved_path)
29 .map(|content| SteelVal::StringV(content.into()))
30 .map_err(|e| format!("Failed to read file '{}': {}", path, e))
31}
32
33fn write_file(path: String, content: String) -> std::result::Result<SteelVal, String> {
35 let resolved_path = resolve_path(&path);
36
37 if let Some(parent) = resolved_path.parent() {
39 fs::create_dir_all(parent).map_err(|e| format!("Failed to create directory: {}", e))?;
40 }
41
42 fs::write(&resolved_path, content)
43 .map(|_| SteelVal::Void)
44 .map_err(|e| format!("Failed to write file '{}': {}", path, e))
45}
46
47fn file_exists(path: String) -> SteelVal {
49 let resolved_path = resolve_path(&path);
50 SteelVal::BoolV(resolved_path.exists())
51}
52
53fn glob_files(pattern: String) -> SteelVal {
55 let resolved_pattern = resolve_path(&pattern);
56 let pattern_str = resolved_pattern.to_string_lossy();
57
58 match glob::glob(&pattern_str) {
59 Ok(paths) => {
60 let files: Vec<SteelVal> = paths
61 .filter_map(|p| p.ok())
62 .map(|p| SteelVal::StringV(p.to_string_lossy().to_string().into()))
63 .collect();
64 SteelVal::ListV(files.into())
65 }
66 Err(e) => {
67 eprintln!("Glob error: {}", e);
68 SteelVal::ListV(vec![].into())
69 }
70 }
71}
72
73fn path_join(parts: Vec<SteelVal>) -> SteelVal {
75 let mut path = PathBuf::new();
76
77 for part in parts {
78 if let SteelVal::StringV(s) = part {
79 path.push(s.to_string());
80 }
81 }
82
83 SteelVal::StringV(path.to_string_lossy().to_string().into())
84}
85
86fn mkdir(path: String) -> std::result::Result<SteelVal, String> {
88 let resolved_path = resolve_path(&path);
89
90 fs::create_dir_all(&resolved_path)
91 .map(|_| SteelVal::Void)
92 .map_err(|e| format!("Failed to create directory '{}': {}", path, e))
93}
94
95fn resolve_path(path: &str) -> PathBuf {
97 let path = PathBuf::from(path);
98
99 if path.is_absolute() {
101 return path;
102 }
103
104 with_context(|ctx| ctx.project_root.join(&path)).unwrap_or(path)
106}