summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUILD.gn2
-rw-r--r--js/deno.ts4
-rw-r--r--js/fileinfo.ts108
-rw-r--r--js/read_dir.ts49
-rw-r--r--js/read_dir_test.ts60
-rw-r--r--js/stat.ts93
-rw-r--r--js/unit_tests.ts1
-rw-r--r--src/handlers.rs58
-rw-r--r--src/msg.fbs12
9 files changed, 294 insertions, 93 deletions
diff --git a/BUILD.gn b/BUILD.gn
index a9953a91d..8ed57ca40 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -75,6 +75,7 @@ ts_sources = [
"js/dom_types.ts",
"js/errors.ts",
"js/fetch.ts",
+ "js/fileinfo.ts",
"js/files.ts",
"js/io.ts",
"js/global-eval.ts",
@@ -88,6 +89,7 @@ ts_sources = [
"js/platform.ts",
"js/plugins.d.ts",
"js/read_file.ts",
+ "js/read_dir.ts",
"js/remove.ts",
"js/rename.ts",
"js/read_link.ts",
diff --git a/js/deno.ts b/js/deno.ts
index 62816b8e1..b4181de14 100644
--- a/js/deno.ts
+++ b/js/deno.ts
@@ -9,9 +9,10 @@ export { makeTempDirSync, makeTempDir } from "./make_temp_dir";
export { removeSync, remove, removeAllSync, removeAll } from "./remove";
export { renameSync, rename } from "./rename";
export { readFileSync, readFile } from "./read_file";
+export { readDirSync, readDir } from "./read_dir";
export { copyFileSync, copyFile } from "./copy_file";
export { readlinkSync, readlink } from "./read_link";
-export { FileInfo, statSync, lstatSync, stat, lstat } from "./stat";
+export { statSync, lstatSync, stat, lstat } from "./stat";
export { symlinkSync, symlink } from "./symlink";
export { writeFileSync, writeFile } from "./write_file";
export { ErrorKind, DenoError } from "./errors";
@@ -19,4 +20,5 @@ export { libdeno } from "./libdeno";
export { platform } from "./platform";
export { trace } from "./trace";
export { truncateSync, truncate } from "./truncate";
+export { FileInfo } from "./fileinfo";
export const args: string[] = [];
diff --git a/js/fileinfo.ts b/js/fileinfo.ts
new file mode 100644
index 000000000..44668974e
--- /dev/null
+++ b/js/fileinfo.ts
@@ -0,0 +1,108 @@
+// Copyright 2018 the Deno authors. All rights reserved. MIT license.
+import * as fbs from "gen/msg_generated";
+
+/**
+ * A FileInfo describes a file and is returned by `stat`, `lstat`,
+ * `statSync`, `lstatSync`.
+ */
+export interface FileInfo {
+ readonly _isFile: boolean;
+ readonly _isSymlink: boolean;
+ /** The size of the file, in bytes. */
+ len: number;
+ /**
+ * The last modification time of the file. This corresponds to the `mtime`
+ * field from `stat` on Unix and `ftLastWriteTime` on Windows. This may not
+ * be available on all platforms.
+ */
+ modified: number | null;
+ /**
+ * The last access time of the file. This corresponds to the `atime`
+ * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
+ * be available on all platforms.
+ */
+ accessed: number | null;
+ /**
+ * The last access time of the file. This corresponds to the `birthtime`
+ * field from `stat` on Unix and `ftCreationTime` on Windows. This may not
+ * be available on all platforms.
+ */
+ created: number | null;
+ /**
+ * The underlying raw st_mode bits that contain the standard Unix permissions
+ * for this file/directory. TODO Match behavior with Go on windows for mode.
+ */
+ mode: number | null;
+
+ /**
+ * Returns the file or directory name.
+ */
+ name: string | null;
+
+ /** Returns the file or directory path. */
+ path: string | null;
+
+ /**
+ * Returns whether this is info for a regular file. This result is mutually
+ * exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
+ */
+ isFile(): boolean;
+
+ /**
+ * Returns whether this is info for a regular directory. This result is
+ * mutually exclusive to `FileInfo.isFile` and `FileInfo.isSymlink`.
+ */
+ isDirectory(): boolean;
+
+ /**
+ * Returns whether this is info for a symlink. This result is
+ * mutually exclusive to `FileInfo.isFile` and `FileInfo.isDirectory`.
+ */
+ isSymlink(): boolean;
+}
+
+export class FileInfoImpl implements FileInfo {
+ readonly _isFile: boolean;
+ readonly _isSymlink: boolean;
+ len: number;
+ modified: number | null;
+ accessed: number | null;
+ created: number | null;
+ mode: number | null;
+ name: string | null;
+ path: string | null;
+
+ /* @internal */
+ constructor(private _msg: fbs.StatRes) {
+ const modified = this._msg.modified().toFloat64();
+ const accessed = this._msg.accessed().toFloat64();
+ const created = this._msg.created().toFloat64();
+ const hasMode = this._msg.hasMode();
+ const mode = this._msg.mode(); // negative for invalid mode (Windows)
+ const name = this._msg.name();
+ const path = this._msg.path();
+
+ this._isFile = this._msg.isFile();
+ this._isSymlink = this._msg.isSymlink();
+ this.len = this._msg.len().toFloat64();
+ this.modified = modified ? modified : null;
+ this.accessed = accessed ? accessed : null;
+ this.created = created ? created : null;
+ // null on Windows
+ this.mode = hasMode ? mode : null;
+ this.name = name ? name : null;
+ this.path = path ? path : null;
+ }
+
+ isFile() {
+ return this._isFile;
+ }
+
+ isDirectory() {
+ return !this._isFile && !this._isSymlink;
+ }
+
+ isSymlink() {
+ return this._isSymlink;
+ }
+}
diff --git a/js/read_dir.ts b/js/read_dir.ts
new file mode 100644
index 000000000..822177aa8
--- /dev/null
+++ b/js/read_dir.ts
@@ -0,0 +1,49 @@
+// Copyright 2018 the Deno authors. All rights reserved. MIT license.
+import * as fbs from "gen/msg_generated";
+import { flatbuffers } from "flatbuffers";
+import * as dispatch from "./dispatch";
+import { FileInfo, FileInfoImpl } from "./fileinfo";
+import { assert } from "./util";
+
+/**
+ * Reads the directory given by path and returns
+ * a list of file info synchronously.
+ *
+ * import { readDirSync } from "deno";
+ * const files = readDirSync("/");
+ */
+export function readDirSync(path: string): FileInfo[] {
+ return res(dispatch.sendSync(...req(path)));
+}
+
+/**
+ * Reads the directory given by path and returns a list of file info.
+ *
+ * import { readDir } from "deno";
+ * const files = await readDir("/");
+ *
+ */
+export async function readDir(path: string): Promise<FileInfo[]> {
+ return res(await dispatch.sendAsync(...req(path)));
+}
+
+function req(path: string): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] {
+ const builder = new flatbuffers.Builder();
+ const path_ = builder.createString(path);
+ fbs.ReadDir.startReadDir(builder);
+ fbs.ReadDir.addPath(builder, path_);
+ const msg = fbs.ReadDir.endReadDir(builder);
+ return [builder, fbs.Any.ReadDir, msg];
+}
+
+function res(baseRes: null | fbs.Base): FileInfo[] {
+ assert(baseRes != null);
+ assert(fbs.Any.ReadDirRes === baseRes!.msgType());
+ const res = new fbs.ReadDirRes();
+ assert(baseRes!.msg(res) != null);
+ const fileInfos: FileInfo[] = [];
+ for (let i = 0; i < res.entriesLength(); i++) {
+ fileInfos.push(new FileInfoImpl(res.entries(i)!));
+ }
+ return fileInfos;
+}
diff --git a/js/read_dir_test.ts b/js/read_dir_test.ts
new file mode 100644
index 000000000..43430ed00
--- /dev/null
+++ b/js/read_dir_test.ts
@@ -0,0 +1,60 @@
+// Copyright 2018 the Deno authors. All rights reserved. MIT license.
+import { test, testPerm, assert, assertEqual } from "./test_util.ts";
+import * as deno from "deno";
+import { FileInfo } from "deno";
+
+function assertSameContent(files: FileInfo[]) {
+ let counter = 0;
+
+ for (const file of files) {
+ if (file.name == "subdir") {
+ assert(file.isDirectory());
+ counter++;
+ }
+
+ if (file.name === "002_hello.ts") {
+ assertEqual(file.path, `tests/${file.name}`);
+ counter++;
+ }
+ }
+
+ assertEqual(counter, 2);
+}
+
+testPerm({ write: true }, function readDirSyncSuccess() {
+ const files = deno.readDirSync("tests/");
+ assertSameContent(files);
+});
+
+test(function readDirSyncNotDir() {
+ let caughtError = false;
+ let src;
+
+ try {
+ src = deno.readDirSync("package.json");
+ } catch (err) {
+ caughtError = true;
+ assertEqual(err.kind, deno.ErrorKind.Other);
+ }
+ assert(caughtError);
+ assertEqual(src, undefined);
+});
+
+test(function readDirSyncNotFound() {
+ let caughtError = false;
+ let src;
+
+ try {
+ src = deno.readDirSync("bad_dir_name");
+ } catch (err) {
+ caughtError = true;
+ assertEqual(err.kind, deno.ErrorKind.NotFound);
+ }
+ assert(caughtError);
+ assertEqual(src, undefined);
+});
+
+testPerm({ write: true }, async function readDirSuccess() {
+ const files = await deno.readDir("tests/");
+ assertSameContent(files);
+});
diff --git a/js/stat.ts b/js/stat.ts
index b2b18c57c..284679f84 100644
--- a/js/stat.ts
+++ b/js/stat.ts
@@ -3,98 +3,7 @@ import * as fbs from "gen/msg_generated";
import { flatbuffers } from "flatbuffers";
import * as dispatch from "./dispatch";
import { assert } from "./util";
-
-/**
- * A FileInfo describes a file and is returned by `stat`, `lstat`,
- * `statSync`, `lstatSync`.
- */
-export interface FileInfo {
- readonly _isFile: boolean;
- readonly _isSymlink: boolean;
- /** The size of the file, in bytes. */
- len: number;
- /**
- * The last modification time of the file. This corresponds to the `mtime`
- * field from `stat` on Unix and `ftLastWriteTime` on Windows. This may not
- * be available on all platforms.
- */
- modified: number | null;
- /**
- * The last access time of the file. This corresponds to the `atime`
- * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
- * be available on all platforms.
- */
- accessed: number | null;
- /**
- * The last access time of the file. This corresponds to the `birthtime`
- * field from `stat` on Unix and `ftCreationTime` on Windows. This may not
- * be available on all platforms.
- */
- created: number | null;
- /**
- * The underlying raw st_mode bits that contain the standard Unix permissions
- * for this file/directory. TODO Match behavior with Go on windows for mode.
- */
- mode: number | null;
-
- /**
- * Returns whether this is info for a regular file. This result is mutually
- * exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
- */
- isFile(): boolean;
-
- /**
- * Returns whether this is info for a regular directory. This result is
- * mutually exclusive to `FileInfo.isFile` and `FileInfo.isSymlink`.
- */
- isDirectory(): boolean;
-
- /**
- * Returns whether this is info for a symlink. This result is
- * mutually exclusive to `FileInfo.isFile` and `FileInfo.isDirectory`.
- */
- isSymlink(): boolean;
-}
-
-class FileInfoImpl implements FileInfo {
- readonly _isFile: boolean;
- readonly _isSymlink: boolean;
- len: number;
- modified: number | null;
- accessed: number | null;
- created: number | null;
- mode: number | null;
-
- /* @internal */
- constructor(private _msg: fbs.StatRes) {
- const modified = this._msg.modified().toFloat64();
- const accessed = this._msg.accessed().toFloat64();
- const created = this._msg.created().toFloat64();
- const hasMode = this._msg.hasMode();
- const mode = this._msg.mode(); // negative for invalid mode (Windows)
-
- this._isFile = this._msg.isFile();
- this._isSymlink = this._msg.isSymlink();
- this.len = this._msg.len().toFloat64();
- this.modified = modified ? modified : null;
- this.accessed = accessed ? accessed : null;
- this.created = created ? created : null;
- // null on Windows
- this.mode = hasMode ? mode : null;
- }
-
- isFile() {
- return this._isFile;
- }
-
- isDirectory() {
- return !this._isFile && !this._isSymlink;
- }
-
- isSymlink() {
- return this._isSymlink;
- }
-}
+import { FileInfo, FileInfoImpl } from "./fileinfo";
/**
* Queries the file system for information on the path provided.
diff --git a/js/unit_tests.ts b/js/unit_tests.ts
index e33fcf245..4ec5a720c 100644
--- a/js/unit_tests.ts
+++ b/js/unit_tests.ts
@@ -7,6 +7,7 @@ import "./fetch_test.ts";
import "./os_test.ts";
import "./files_test.ts";
import "./read_file_test.ts";
+import "./read_dir_test.ts";
import "./write_file_test.ts";
import "./copy_file_test.ts";
import "./mkdir_test.ts";
diff --git a/src/handlers.rs b/src/handlers.rs
index c7e7ab7c5..80eb2871e 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -76,6 +76,7 @@ pub fn msg_from_js(
msg::Any::Write => handle_write,
msg::Any::Remove => handle_remove,
msg::Any::ReadFile => handle_read_file,
+ msg::Any::ReadDir => handle_read_dir,
msg::Any::Rename => handle_rename,
msg::Any::Readlink => handle_read_link,
msg::Any::Symlink => handle_symlink,
@@ -816,6 +817,63 @@ fn handle_stat(
})
}
+fn handle_read_dir(
+ _state: Arc<IsolateState>,
+ base: &msg::Base,
+ data: &'static mut [u8],
+) -> Box<Op> {
+ assert_eq!(data.len(), 0);
+ let msg = base.msg_as_read_dir().unwrap();
+ let cmd_id = base.cmd_id();
+ let path = String::from(msg.path().unwrap());
+
+ blocking!(base.sync(), || -> OpResult {
+ debug!("handle_read_dir {}", path);
+ let builder = &mut FlatBufferBuilder::new();
+ let entries: Vec<_> = fs::read_dir(Path::new(&path))?
+ .map(|entry| {
+ let entry = entry.unwrap();
+ let metadata = entry.metadata().unwrap();
+ let file_type = metadata.file_type();
+ let name = builder.create_string(entry.file_name().to_str().unwrap());
+ let path = builder.create_string(entry.path().to_str().unwrap());
+
+ msg::StatRes::create(
+ builder,
+ &msg::StatResArgs {
+ is_file: file_type.is_file(),
+ is_symlink: file_type.is_symlink(),
+ len: metadata.len(),
+ modified: to_seconds!(metadata.modified()),
+ accessed: to_seconds!(metadata.accessed()),
+ created: to_seconds!(metadata.created()),
+ name: Some(name),
+ path: Some(path),
+ ..Default::default()
+ },
+ )
+ }).collect();
+
+ let entries = builder.create_vector(&entries);
+ let msg = msg::ReadDirRes::create(
+ builder,
+ &msg::ReadDirResArgs {
+ entries: Some(entries),
+ ..Default::default()
+ },
+ );
+ Ok(serialize_response(
+ cmd_id,
+ builder,
+ msg::BaseArgs {
+ msg: Some(msg.as_union_value()),
+ msg_type: msg::Any::ReadDirRes,
+ ..Default::default()
+ },
+ ))
+ })
+}
+
fn handle_write_file(
state: Arc<IsolateState>,
base: &msg::Base,
diff --git a/src/msg.fbs b/src/msg.fbs
index e4886b6a9..83dca11e6 100644
--- a/src/msg.fbs
+++ b/src/msg.fbs
@@ -16,6 +16,8 @@ union Any {
Remove,
ReadFile,
ReadFileRes,
+ ReadDir,
+ ReadDirRes,
WriteFile,
CopyFile,
Rename,
@@ -191,6 +193,14 @@ table ReadFileRes {
data: [ubyte];
}
+table ReadDir {
+ path: string;
+}
+
+table ReadDirRes {
+ entries: [StatRes];
+}
+
table WriteFile {
filename: string;
data: [ubyte];
@@ -235,6 +245,8 @@ table StatRes {
created:ulong;
mode: uint;
has_mode: bool; // false on windows
+ name: string;
+ path: string;
}
table Truncate {