diff --git a/cli/js/deno_unstable.ts b/cli/js/deno_unstable.ts index 967eafdcd8..2327b554d2 100644 --- a/cli/js/deno_unstable.ts +++ b/cli/js/deno_unstable.ts @@ -4,6 +4,7 @@ export { umask } from "./ops/fs/umask.ts"; export { linkSync, link } from "./ops/fs/link.ts"; +export { fstatSync, fstat } from "./ops/fs/stat.ts"; export { fsyncSync, fsync } from "./ops/fs/sync.ts"; export { symlinkSync, symlink } from "./ops/fs/symlink.ts"; export { loadavg, osRelease, hostname } from "./ops/os.ts"; diff --git a/cli/js/lib.deno.unstable.d.ts b/cli/js/lib.deno.unstable.d.ts index daecf66045..cc7dea9870 100644 --- a/cli/js/lib.deno.unstable.d.ts +++ b/cli/js/lib.deno.unstable.d.ts @@ -1142,4 +1142,26 @@ declare namespace Deno { * ``` */ export function fsync(rid: number): Promise; + + /** **UNSTABLE**: New API, yet to be vetted. + * Synchronously returns a `Deno.FileInfo` for the given file stream. + * + * ```ts + * const file = Deno.openSync("file.txt", { read: true }); + * const fileInfo = Deno.fstatSync(file.rid); + * assert(fileInfo.isFile); + * ``` + */ + export function fstatSync(rid: number): FileInfo; + + /** **UNSTABLE**: New API, yet to be vetted. + * Returns a `Deno.FileInfo` for the given file stream. + * + * ```ts + * const file = await Deno.open("file.txt", { read: true }); + * const fileInfo = await Deno.fstat(file.rid); + * assert(fileInfo.isFile); + * ``` + */ + export function fstat(rid: number): Promise; } diff --git a/cli/js/ops/fs/stat.ts b/cli/js/ops/fs/stat.ts index 93d31fc3f8..402adeafc3 100644 --- a/cli/js/ops/fs/stat.ts +++ b/cli/js/ops/fs/stat.ts @@ -66,6 +66,14 @@ export function parseFileInfo(response: StatResponse): FileInfo { }; } +export function fstatSync(rid: number): FileInfo { + return parseFileInfo(sendSync("op_fstat", { rid })); +} + +export async function fstat(rid: number): Promise { + return parseFileInfo(await sendAsync("op_fstat", { rid })); +} + export async function lstat(path: string | URL): Promise { path = pathFromURL(path); const res = (await sendAsync("op_stat", { diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs index 9417c112d7..7c81006050 100644 --- a/cli/ops/fs.rs +++ b/cli/ops/fs.rs @@ -23,6 +23,7 @@ pub fn init(i: &mut CoreIsolate, s: &State) { i.register_op("op_open", s.stateful_json_op2(op_open)); i.register_op("op_seek", s.stateful_json_op2(op_seek)); i.register_op("op_fsync", s.stateful_json_op2(op_fsync)); + i.register_op("op_fstat", s.stateful_json_op2(op_fstat)); i.register_op("op_umask", s.stateful_json_op(op_umask)); i.register_op("op_chdir", s.stateful_json_op(op_chdir)); i.register_op("op_mkdir", s.stateful_json_op(op_mkdir)); @@ -250,6 +251,51 @@ fn op_fsync( } } +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct FstatArgs { + promise_id: Option, + rid: i32, +} + +fn op_fstat( + isolate_state: &mut CoreIsolateState, + state: &State, + args: Value, + _zero_copy: &mut [ZeroCopyBuf], +) -> Result { + state.check_unstable("Deno.fstat"); + let args: FstatArgs = serde_json::from_value(args)?; + let rid = args.rid as u32; + + let resource_table = isolate_state.resource_table.clone(); + let is_sync = args.promise_id.is_none(); + + if is_sync { + let mut resource_table = resource_table.borrow_mut(); + let metadata = std_file_resource(&mut resource_table, rid, |r| match r { + Ok(std_file) => std_file.metadata().map_err(OpError::from), + Err(_) => Err(OpError::type_error( + "cannot stat this type of resource".to_string(), + )), + })?; + Ok(JsonOp::Sync(get_stat_json(metadata).unwrap())) + } else { + let fut = async move { + let mut resource_table = resource_table.borrow_mut(); + let metadata = + std_file_resource(&mut resource_table, rid, |r| match r { + Ok(std_file) => std_file.metadata().map_err(OpError::from), + Err(_) => Err(OpError::type_error( + "cannot stat this type of resource".to_string(), + )), + })?; + Ok(get_stat_json(metadata).unwrap()) + }; + Ok(JsonOp::Async(fut.boxed_local())) + } +} + #[derive(Deserialize)] struct UmaskArgs { mask: Option, diff --git a/cli/tests/unit/stat_test.ts b/cli/tests/unit/stat_test.ts index 67598a2d79..b329492864 100644 --- a/cli/tests/unit/stat_test.ts +++ b/cli/tests/unit/stat_test.ts @@ -6,6 +6,36 @@ import { pathToAbsoluteFileUrl, } from "./test_util.ts"; +unitTest({ perms: { read: true } }, function fstatSyncSuccess(): void { + const file = Deno.openSync("README.md"); + const fileInfo = Deno.fstatSync(file.rid); + assert(fileInfo.isFile); + assert(!fileInfo.isSymlink); + assert(!fileInfo.isDirectory); + assert(fileInfo.size); + assert(fileInfo.atime); + assert(fileInfo.mtime); + assert(fileInfo.birthtime); + + Deno.close(file.rid); +}); + +unitTest({ perms: { read: true } }, async function fstatSuccess(): Promise< + void +> { + const file = await Deno.open("README.md"); + const fileInfo = await Deno.fstat(file.rid); + assert(fileInfo.isFile); + assert(!fileInfo.isSymlink); + assert(!fileInfo.isDirectory); + assert(fileInfo.size); + assert(fileInfo.atime); + assert(fileInfo.mtime); + assert(fileInfo.birthtime); + + Deno.close(file.rid); +}); + unitTest( { perms: { read: true, write: true } }, function statSyncSuccess(): void {