1mod access;
4mod chmod;
5mod file_handle;
6mod guard;
7mod mkdir;
8mod open;
9mod read_dir;
10mod read_file;
11mod rename;
12mod rm;
13mod stats;
14mod symlink;
15mod write_file;
16
17use crate::utils::module::ModuleInfo;
18use rquickjs::Value;
19use rquickjs::{
20 module::{Declarations, Exports, ModuleDef},
21 prelude::{Async, Func},
22};
23use rquickjs::{Class, Ctx, Object, Result};
24
25use self::file_handle::FileHandle;
26use self::guard::*;
27use self::read_dir::Dirent;
28use self::stats::Stats;
29
30pub const CONSTANT_F_OK: u32 = 0;
31pub const CONSTANT_R_OK: u32 = 4;
32pub const CONSTANT_W_OK: u32 = 2;
33pub const CONSTANT_X_OK: u32 = 1;
34
35pub struct FsPromisesModule;
36
37impl ModuleDef for FsPromisesModule {
38 fn declare(declare: &Declarations) -> Result<()> {
39 declare.declare("access")?;
40 declare.declare("open")?;
41 declare.declare("readFile")?;
42 declare.declare("writeFile")?;
43 declare.declare("rename")?;
44 declare.declare("readdir")?;
45 declare.declare("mkdir")?;
46 declare.declare("mkdtemp")?;
47 declare.declare("rm")?;
48 declare.declare("rmdir")?;
49 declare.declare("stat")?;
50 declare.declare("lstat")?;
51 declare.declare("constants")?;
52 declare.declare("chmod")?;
53 declare.declare("symlink")?;
54
55 declare.declare("default")?;
56
57 Ok(())
58 }
59
60 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
61 export_object(exports, &fs_promises_object(ctx)?)
65 }
66}
67
68impl From<FsPromisesModule> for ModuleInfo<FsPromisesModule> {
69 fn from(val: FsPromisesModule) -> Self {
70 ModuleInfo {
71 name: "fs/promises",
72 module: val,
73 }
74 }
75}
76
77pub struct FsModule;
78
79impl ModuleDef for FsModule {
80 fn declare(declare: &Declarations) -> Result<()> {
81 declare.declare("promises")?;
82 declare.declare("accessSync")?;
83 declare.declare("mkdirSync")?;
84 declare.declare("mkdtempSync")?;
85 declare.declare("readdirSync")?;
86 declare.declare("readFileSync")?;
87 declare.declare("existsSync")?;
88 declare.declare("rmdirSync")?;
89 declare.declare("rmSync")?;
90 declare.declare("statSync")?;
91 declare.declare("lstatSync")?;
92 declare.declare("writeFileSync")?;
93 declare.declare("constants")?;
94 declare.declare("chmodSync")?;
95 declare.declare("renameSync")?;
96 declare.declare("symlinkSync")?;
97
98 declare.declare("default")?;
99
100 Ok(())
101 }
102
103 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
104 export_object(exports, &fs_object(ctx)?)
105 }
106}
107
108pub fn fill_fs<'js>(ctx: &Ctx<'js>, target: &Object<'js>) -> Result<()> {
114 let promises = Object::new(ctx.clone())?;
115 export_promises(ctx, &promises)?;
116 export_constants(ctx, target)?;
117
118 target.set("promises", promises)?;
122 target.set("accessSync", Func::from(access_sync_guarded))?;
123 target.set("mkdirSync", Func::from(mkdir_sync_guarded))?;
124 target.set("mkdtempSync", Func::from(mkdtemp_sync_guarded))?;
125 target.set("readdirSync", Func::from(read_dir_sync_guarded))?;
126 target.set("readFileSync", Func::from(read_file_sync_guarded))?;
127 target.set("existsSync", Func::from(exists_sync_guarded))?;
128 target.set("rmdirSync", Func::from(rmdir_sync_guarded))?;
129 target.set("rmSync", Func::from(rmfile_sync_guarded))?;
130 target.set("statSync", Func::from(stat_sync_guarded))?;
131 target.set("lstatSync", Func::from(lstat_sync_guarded))?;
132 target.set("writeFileSync", Func::from(write_file_sync_guarded))?;
133 target.set("chmodSync", Func::from(chmod_sync_guarded))?;
134 target.set("renameSync", Func::from(rename_sync_guarded))?;
135 target.set("symlinkSync", Func::from(symlink_sync_guarded))?;
136
137 Ok(())
138}
139
140pub fn fs_object<'js>(ctx: &Ctx<'js>) -> Result<Object<'js>> {
148 cached(ctx, false)
149}
150
151pub fn fs_promises_object<'js>(ctx: &Ctx<'js>) -> Result<Object<'js>> {
153 cached(ctx, true)
154}
155
156struct FsNamespaces {
157 fs: rquickjs::Persistent<Object<'static>>,
158 promises: rquickjs::Persistent<Object<'static>>,
159}
160
161#[allow(unsafe_code)]
164unsafe impl rquickjs::JsLifetime<'_> for FsNamespaces {
165 type Changed<'to> = FsNamespaces;
166}
167
168fn cached<'js>(ctx: &Ctx<'js>, promises: bool) -> Result<Object<'js>> {
169 if let Some(ud) = ctx.userdata::<FsNamespaces>() {
170 let held = if promises { ud.promises.clone() } else { ud.fs.clone() };
171 if let Ok(obj) = held.restore(ctx) {
172 return Ok(obj);
173 }
174 }
175 define_classes(ctx)?;
176 let fs = Object::new(ctx.clone())?;
177 fill_fs(ctx, &fs)?;
178 let promises_obj = Object::new(ctx.clone())?;
179 export_promises(ctx, &promises_obj)?;
180 fs.set("promises", promises_obj.clone())?;
183 let answer = if promises { promises_obj.clone() } else { fs.clone() };
184 let _ = ctx.store_userdata(FsNamespaces {
185 fs: rquickjs::Persistent::save(ctx, fs),
186 promises: rquickjs::Persistent::save(ctx, promises_obj),
187 });
188 Ok(answer)
189}
190
191fn define_classes(ctx: &Ctx<'_>) -> Result<()> {
194 let globals = ctx.globals();
195 Class::<Dirent>::define(&globals)?;
196 Class::<FileHandle>::define(&globals)?;
197 Class::<Stats>::define(&globals)?;
198 Ok(())
199}
200
201fn export_promises<'js>(ctx: &Ctx<'js>, exports: &Object<'js>) -> Result<()> {
202 export_constants(ctx, exports)?;
203
204 exports.set("access", Func::from(Async(access_guarded)))?;
205 exports.set("open", Func::from(Async(open_guarded)))?;
206 exports.set("readFile", Func::from(Async(read_file_guarded)))?;
207 exports.set("writeFile", Func::from(Async(write_file_guarded)))?;
208 exports.set("rename", Func::from(Async(rename_guarded)))?;
209 exports.set("readdir", Func::from(Async(read_dir_guarded)))?;
210 exports.set("mkdir", Func::from(Async(mkdir_guarded)))?;
211 exports.set("mkdtemp", Func::from(Async(mkdtemp_guarded)))?;
212 exports.set("rm", Func::from(Async(rmfile_guarded)))?;
213 exports.set("rmdir", Func::from(Async(rmdir_guarded)))?;
214 exports.set("stat", Func::from(Async(stat_guarded)))?;
215 exports.set("lstat", Func::from(Async(lstat_guarded)))?;
216 exports.set("chmod", Func::from(Async(chmod_guarded)))?;
217 exports.set("symlink", Func::from(Async(symlink_guarded)))?;
218
219 Ok(())
220}
221
222fn export_constants<'js>(ctx: &Ctx<'js>, exports: &Object<'js>) -> Result<()> {
223 let constants = Object::new(ctx.clone())?;
224 constants.set("F_OK", CONSTANT_F_OK)?;
225 constants.set("R_OK", CONSTANT_R_OK)?;
226 constants.set("W_OK", CONSTANT_W_OK)?;
227 constants.set("X_OK", CONSTANT_X_OK)?;
228
229 exports.set("constants", constants)?;
230
231 Ok(())
232}
233
234impl From<FsModule> for ModuleInfo<FsModule> {
235 fn from(val: FsModule) -> Self {
236 ModuleInfo {
237 name: "fs",
238 module: val,
239 }
240 }
241}
242
243pub fn init(ctx: &Ctx<'_>) -> Result<()> {
254 ctx.globals().set("fs", fs_object(ctx)?)
255}
256
257fn export_object<'js>(exports: &Exports<'js>, namespace: &Object<'js>) -> Result<()> {
263 for name in namespace.keys::<String>() {
264 let name = name?;
265 let value: Value<'js> = namespace.get(&name)?;
266 exports.export(name, value)?;
267 }
268 exports.export("default", namespace.clone())?;
269 Ok(())
270}