use rquickjs::{function::Opt, Ctx, Exception, Object};
use crate::js::rquickjs_ext::ObjectExt;
async fn stat_fn(ctx: Ctx<'_>, path: String) -> rquickjs::Result<Object<'_>> {
let meta = tokio::fs::metadata(&path)
.await
.map_err(|e| Exception::throw_message(&ctx, &format!("fs.stat: {e}")))?;
let obj = Object::new(ctx.clone())?;
obj.set("size", meta.len() as f64)?;
obj.set("isDir", meta.is_dir())?;
obj.set("isFile", meta.is_file())?;
obj.set("isSymlink", meta.file_type().is_symlink())?;
let mtime = meta
.modified()
.ok()
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_millis() as f64)
.unwrap_or(0.0);
obj.set("mtime", mtime)?;
Ok(obj)
}
pub fn init(ctx: Ctx<'_>) -> rquickjs::Result<Object<'_>> {
let obj = Object::new(ctx.clone())?;
obj.def_fn_async("read", async |ctx: Ctx<'_>, path: String| {
tokio::fs::read_to_string(&path)
.await
.map_err(|e| Exception::throw_message(&ctx, &format!("fs.read: {e}")))
})?;
obj.def_fn_async(
"write",
async |ctx: Ctx<'_>, path: String, content: String| {
tokio::fs::write(&path, content)
.await
.map_err(|e| Exception::throw_message(&ctx, &format!("fs.write: {e}")))
},
)?;
obj.def_fn_async("exists", async |path: String| {
tokio::fs::metadata(&path).await.is_ok()
})?;
obj.def_fn_async(
"mkdir",
async |ctx: Ctx<'_>, path: String, Opt(opts): Opt<Object<'_>>| {
let recursive = opts
.as_ref()
.and_then(|o| o.get::<_, bool>("recursive").ok())
.unwrap_or(false);
if recursive {
tokio::fs::create_dir_all(&path).await
} else {
tokio::fs::create_dir(&path).await
}
.map_err(|e| Exception::throw_message(&ctx, &format!("fs.mkdir: {e}")))
},
)?;
obj.def_fn_async(
"rm",
async |ctx: Ctx<'_>, path: String, Opt(opts): Opt<Object<'_>>| {
let recursive = opts
.as_ref()
.and_then(|o| o.get::<_, bool>("recursive").ok())
.unwrap_or(false);
let res = if recursive {
match tokio::fs::remove_dir_all(&path).await {
Ok(()) => Ok(()),
Err(_) => tokio::fs::remove_file(&path).await,
}
} else {
match tokio::fs::remove_file(&path).await {
Ok(()) => Ok(()),
Err(_) => tokio::fs::remove_dir(&path).await,
}
};
res.map_err(|e| Exception::throw_message(&ctx, &format!("fs.rm: {e}")))
},
)?;
obj.def_fn_async("readDir", async |ctx: Ctx<'_>, path: String| {
let mut entries = Vec::new();
let mut dir = tokio::fs::read_dir(&path).await.map_err(|e| {
Exception::throw_message(&ctx, &format!("fs.readDir: {e}"))
})?;
while let Some(entry) = dir.next_entry().await.map_err(|e| {
Exception::throw_message(&ctx, &format!("fs.readDir: {e}"))
})? {
entries.push(entry.file_name().to_string_lossy().to_string());
}
Ok::<_, rquickjs::Error>(entries)
})?;
obj.def_fn_async("stat", stat_fn)?;
obj.def_fn_async(
"rename",
async |ctx: Ctx<'_>, from: String, to: String| {
tokio::fs::rename(&from, &to)
.await
.map_err(|e| Exception::throw_message(&ctx, &format!("fs.rename: {e}")))
},
)?;
obj.def_fn_async(
"copy",
async |ctx: Ctx<'_>, from: String, to: String| {
tokio::fs::copy(&from, &to)
.await
.map(|_| ())
.map_err(|e| Exception::throw_message(&ctx, &format!("fs.copy: {e}")))
},
)?;
Ok(obj)
}