mod access;
mod chmod;
mod file_handle;
mod guard;
mod mkdir;
mod open;
mod read_dir;
mod read_file;
mod rename;
mod rm;
mod stats;
mod symlink;
mod write_file;
use crate::utils::module::ModuleInfo;
use rquickjs::Value;
use rquickjs::{
module::{Declarations, Exports, ModuleDef},
prelude::{Async, Func},
};
use rquickjs::{Class, Ctx, Object, Result};
use self::file_handle::FileHandle;
use self::guard::*;
use self::read_dir::Dirent;
use self::stats::Stats;
pub const CONSTANT_F_OK: u32 = 0;
pub const CONSTANT_R_OK: u32 = 4;
pub const CONSTANT_W_OK: u32 = 2;
pub const CONSTANT_X_OK: u32 = 1;
pub struct FsPromisesModule;
impl ModuleDef for FsPromisesModule {
fn declare(declare: &Declarations) -> Result<()> {
declare.declare("access")?;
declare.declare("open")?;
declare.declare("readFile")?;
declare.declare("writeFile")?;
declare.declare("rename")?;
declare.declare("readdir")?;
declare.declare("mkdir")?;
declare.declare("mkdtemp")?;
declare.declare("rm")?;
declare.declare("rmdir")?;
declare.declare("stat")?;
declare.declare("lstat")?;
declare.declare("constants")?;
declare.declare("chmod")?;
declare.declare("symlink")?;
declare.declare("default")?;
Ok(())
}
fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
export_object(exports, &fs_promises_object(ctx)?)
}
}
impl From<FsPromisesModule> for ModuleInfo<FsPromisesModule> {
fn from(val: FsPromisesModule) -> Self {
ModuleInfo {
name: "fs/promises",
module: val,
}
}
}
pub struct FsModule;
impl ModuleDef for FsModule {
fn declare(declare: &Declarations) -> Result<()> {
declare.declare("promises")?;
declare.declare("accessSync")?;
declare.declare("mkdirSync")?;
declare.declare("mkdtempSync")?;
declare.declare("readdirSync")?;
declare.declare("readFileSync")?;
declare.declare("existsSync")?;
declare.declare("rmdirSync")?;
declare.declare("rmSync")?;
declare.declare("statSync")?;
declare.declare("lstatSync")?;
declare.declare("writeFileSync")?;
declare.declare("constants")?;
declare.declare("chmodSync")?;
declare.declare("renameSync")?;
declare.declare("symlinkSync")?;
declare.declare("default")?;
Ok(())
}
fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
export_object(exports, &fs_object(ctx)?)
}
}
pub fn fill_fs<'js>(ctx: &Ctx<'js>, target: &Object<'js>) -> Result<()> {
let promises = Object::new(ctx.clone())?;
export_promises(ctx, &promises)?;
export_constants(ctx, target)?;
target.set("promises", promises)?;
target.set("accessSync", Func::from(access_sync_guarded))?;
target.set("mkdirSync", Func::from(mkdir_sync_guarded))?;
target.set("mkdtempSync", Func::from(mkdtemp_sync_guarded))?;
target.set("readdirSync", Func::from(read_dir_sync_guarded))?;
target.set("readFileSync", Func::from(read_file_sync_guarded))?;
target.set("existsSync", Func::from(exists_sync_guarded))?;
target.set("rmdirSync", Func::from(rmdir_sync_guarded))?;
target.set("rmSync", Func::from(rmfile_sync_guarded))?;
target.set("statSync", Func::from(stat_sync_guarded))?;
target.set("lstatSync", Func::from(lstat_sync_guarded))?;
target.set("writeFileSync", Func::from(write_file_sync_guarded))?;
target.set("chmodSync", Func::from(chmod_sync_guarded))?;
target.set("renameSync", Func::from(rename_sync_guarded))?;
target.set("symlinkSync", Func::from(symlink_sync_guarded))?;
Ok(())
}
pub fn fs_object<'js>(ctx: &Ctx<'js>) -> Result<Object<'js>> {
cached(ctx, false)
}
pub fn fs_promises_object<'js>(ctx: &Ctx<'js>) -> Result<Object<'js>> {
cached(ctx, true)
}
struct FsNamespaces {
fs: rquickjs::Persistent<Object<'static>>,
promises: rquickjs::Persistent<Object<'static>>,
}
#[allow(unsafe_code)]
unsafe impl rquickjs::JsLifetime<'_> for FsNamespaces {
type Changed<'to> = FsNamespaces;
}
fn cached<'js>(ctx: &Ctx<'js>, promises: bool) -> Result<Object<'js>> {
if let Some(ud) = ctx.userdata::<FsNamespaces>() {
let held = if promises { ud.promises.clone() } else { ud.fs.clone() };
if let Ok(obj) = held.restore(ctx) {
return Ok(obj);
}
}
define_classes(ctx)?;
let fs = Object::new(ctx.clone())?;
fill_fs(ctx, &fs)?;
let promises_obj = Object::new(ctx.clone())?;
export_promises(ctx, &promises_obj)?;
fs.set("promises", promises_obj.clone())?;
let answer = if promises { promises_obj.clone() } else { fs.clone() };
let _ = ctx.store_userdata(FsNamespaces {
fs: rquickjs::Persistent::save(ctx, fs),
promises: rquickjs::Persistent::save(ctx, promises_obj),
});
Ok(answer)
}
fn define_classes(ctx: &Ctx<'_>) -> Result<()> {
let globals = ctx.globals();
Class::<Dirent>::define(&globals)?;
Class::<FileHandle>::define(&globals)?;
Class::<Stats>::define(&globals)?;
Ok(())
}
fn export_promises<'js>(ctx: &Ctx<'js>, exports: &Object<'js>) -> Result<()> {
export_constants(ctx, exports)?;
exports.set("access", Func::from(Async(access_guarded)))?;
exports.set("open", Func::from(Async(open_guarded)))?;
exports.set("readFile", Func::from(Async(read_file_guarded)))?;
exports.set("writeFile", Func::from(Async(write_file_guarded)))?;
exports.set("rename", Func::from(Async(rename_guarded)))?;
exports.set("readdir", Func::from(Async(read_dir_guarded)))?;
exports.set("mkdir", Func::from(Async(mkdir_guarded)))?;
exports.set("mkdtemp", Func::from(Async(mkdtemp_guarded)))?;
exports.set("rm", Func::from(Async(rmfile_guarded)))?;
exports.set("rmdir", Func::from(Async(rmdir_guarded)))?;
exports.set("stat", Func::from(Async(stat_guarded)))?;
exports.set("lstat", Func::from(Async(lstat_guarded)))?;
exports.set("chmod", Func::from(Async(chmod_guarded)))?;
exports.set("symlink", Func::from(Async(symlink_guarded)))?;
Ok(())
}
fn export_constants<'js>(ctx: &Ctx<'js>, exports: &Object<'js>) -> Result<()> {
let constants = Object::new(ctx.clone())?;
constants.set("F_OK", CONSTANT_F_OK)?;
constants.set("R_OK", CONSTANT_R_OK)?;
constants.set("W_OK", CONSTANT_W_OK)?;
constants.set("X_OK", CONSTANT_X_OK)?;
exports.set("constants", constants)?;
Ok(())
}
impl From<FsModule> for ModuleInfo<FsModule> {
fn from(val: FsModule) -> Self {
ModuleInfo {
name: "fs",
module: val,
}
}
}
pub fn init(ctx: &Ctx<'_>) -> Result<()> {
ctx.globals().set("fs", fs_object(ctx)?)
}
fn export_object<'js>(exports: &Exports<'js>, namespace: &Object<'js>) -> Result<()> {
for name in namespace.keys::<String>() {
let name = name?;
let value: Value<'js> = namespace.get(&name)?;
exports.export(name, value)?;
}
exports.export("default", namespace.clone())?;
Ok(())
}