summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/files.ts31
-rw-r--r--js/files_test.ts83
-rw-r--r--src/msg.fbs1
-rw-r--r--src/ops.rs42
4 files changed, 151 insertions, 6 deletions
diff --git a/js/files.ts b/js/files.ts
index ef0037511..4392eadf7 100644
--- a/js/files.ts
+++ b/js/files.ts
@@ -29,14 +29,37 @@ export const stdout = new File(1);
/** An instance of `File` for stderr. */
export const stderr = new File(2);
-// TODO This is just a placeholder - not final API.
-export type OpenMode = "r" | "w" | "w+" | "x";
+type OpenMode =
+ /** Read-only. Default. Starts at beginning of file. */
+ | "r"
+ /** Read-write. Start at beginning of file. */
+ | "r+"
+ /** Write-only. Opens and truncates existing file or creates new one for
+ * writing only.
+ */
+ | "w"
+ /** Read-write. Opens and truncates existing file or creates new one for
+ * writing and reading.
+ */
+ | "w+"
+ /** Write-only. Opens existing file or creates new one. Each write appends
+ * content to the end of file.
+ */
+ | "a"
+ /** Read-write. Behaves like "a" and allows to read from file. */
+ | "a+"
+ /** Write-only. Exclusive create - creates new file only if one doesn't exist
+ * already.
+ */
+ | "x"
+ /** Read-write. Behaves like `x` and allows to read from file. */
+ | "x+";
/** A factory function for creating instances of `File` associated with the
* supplied file name.
*/
export function create(filename: string): Promise<File> {
- return open(filename, "x");
+ return open(filename, "w+");
}
/** Open a file and return an instance of the `File` object.
@@ -52,8 +75,10 @@ export async function open(
): Promise<File> {
const builder = flatbuffers.createBuilder();
const filename_ = builder.createString(filename);
+ const mode_ = builder.createString(mode);
msg.Open.startOpen(builder);
msg.Open.addFilename(builder, filename_);
+ msg.Open.addMode(builder, mode_);
const inner = msg.Open.endOpen(builder);
const baseRes = await dispatch.sendAsync(builder, msg.Any.Open, inner);
assert(baseRes != null);
diff --git a/js/files_test.ts b/js/files_test.ts
index 37f63d9fa..d46a46906 100644
--- a/js/files_test.ts
+++ b/js/files_test.ts
@@ -1,6 +1,6 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import * as deno from "deno";
-import { test, assert, assertEqual } from "./test_util.ts";
+import { test, testPerm, assert, assertEqual } from "./test_util.ts";
test(function filesStdioFileDescriptors() {
assertEqual(deno.stdin.rid, 0);
@@ -29,3 +29,84 @@ test(async function filesToAsyncIterator() {
assertEqual(totalSize, 12);
});
+
+testPerm({ write: true }, async function createFile() {
+ const tempDir = await deno.makeTempDir();
+ const filename = tempDir + "/test.txt";
+ let f = await deno.open(filename, "w");
+ let fileInfo = deno.statSync(filename);
+ assert(fileInfo.isFile());
+ assert(fileInfo.len === 0);
+ const enc = new TextEncoder();
+ const data = enc.encode("Hello");
+ await f.write(data);
+ fileInfo = deno.statSync(filename);
+ assert(fileInfo.len === 5);
+ f.close();
+
+ // TODO: test different modes
+ await deno.removeAll(tempDir);
+});
+
+testPerm({ write: true }, async function openModeWrite() {
+ const tempDir = deno.makeTempDirSync();
+ const encoder = new TextEncoder();
+ const filename = tempDir + "hello.txt";
+ const data = encoder.encode("Hello world!\n");
+
+ let file = await deno.open(filename, "w");
+ // assert file was created
+ let fileInfo = deno.statSync(filename);
+ assert(fileInfo.isFile());
+ assertEqual(fileInfo.len, 0);
+ // write some data
+ await file.write(data);
+ fileInfo = deno.statSync(filename);
+ assertEqual(fileInfo.len, 13);
+ // assert we can't read from file
+ let thrown = false;
+ try {
+ const buf = new Uint8Array(20);
+ await file.read(buf);
+ } catch (e) {
+ thrown = true;
+ } finally {
+ assert(thrown, "'w' mode shouldn't allow to read file");
+ }
+ file.close();
+ // assert that existing file is truncated on open
+ file = await deno.open(filename, "w");
+ file.close();
+ const fileSize = deno.statSync(filename).len;
+ assertEqual(fileSize, 0);
+ await deno.removeAll(tempDir);
+});
+
+testPerm({ write: true }, async function openModeWriteRead() {
+ const tempDir = deno.makeTempDirSync();
+ const encoder = new TextEncoder();
+ const filename = tempDir + "hello.txt";
+ const data = encoder.encode("Hello world!\n");
+
+ let file = await deno.open(filename, "w+");
+ // assert file was created
+ let fileInfo = deno.statSync(filename);
+ assert(fileInfo.isFile());
+ assertEqual(fileInfo.len, 0);
+ // write some data
+ await file.write(data);
+ fileInfo = deno.statSync(filename);
+ assertEqual(fileInfo.len, 13);
+
+ // TODO: this test is not working, I expect because
+ // file handle points to the end of file, but ATM
+ // deno has no seek implementation on Rust side
+ // assert file can be read
+ // const buf = new Uint8Array(20);
+ // const result = await file.read(buf);
+ // console.log(result.eof, result.nread);
+ // assertEqual(result.nread, 13);
+ // file.close();
+
+ await deno.removeAll(tempDir);
+});
diff --git a/src/msg.fbs b/src/msg.fbs
index dcec126dd..9cdb628a1 100644
--- a/src/msg.fbs
+++ b/src/msg.fbs
@@ -348,6 +348,7 @@ table Truncate {
table Open {
filename: string;
perm: uint;
+ mode: string;
}
table OpenRes {
diff --git a/src/ops.rs b/src/ops.rs
index 6d41fbe63..3cf466d0d 100644
--- a/src/ops.rs
+++ b/src/ops.rs
@@ -584,9 +584,47 @@ fn op_open(
let cmd_id = base.cmd_id();
let inner = base.inner_as_open().unwrap();
let filename = PathBuf::from(inner.filename().unwrap());
- // TODO let perm = inner.perm();
+ // let perm = inner.perm();
+ let mode = inner.mode().unwrap();
- let op = tokio::fs::File::open(filename)
+ let mut open_options = tokio::fs::OpenOptions::new();
+
+ match mode {
+ "r" => {
+ open_options.read(true);
+ }
+ "r+" => {
+ open_options.read(true).write(true);
+ }
+ "w" => {
+ open_options.create(true).write(true).truncate(true);
+ }
+ "w+" => {
+ open_options
+ .read(true)
+ .create(true)
+ .write(true)
+ .truncate(true);
+ }
+ "a" => {
+ open_options.create(true).append(true);
+ }
+ "a+" => {
+ open_options.read(true).create(true).append(true);
+ }
+ "x" => {
+ open_options.create_new(true).write(true);
+ }
+ "x+" => {
+ open_options.create_new(true).read(true).write(true);
+ }
+ &_ => {
+ panic!("Unknown file open mode.");
+ }
+ }
+
+ let op = open_options
+ .open(filename)
.map_err(DenoError::from)
.and_then(move |fs_file| -> OpResult {
let resource = resources::add_fs_file(fs_file);