Skip to content

Commit

Permalink
Flesh out open() modes (#1282)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartlomieju authored and ry committed Dec 12, 2018
1 parent e31866a commit c2b91a3
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 6 deletions.
31 changes: 28 additions & 3 deletions js/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand Down
83 changes: 82 additions & 1 deletion js/files_test.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand Down Expand Up @@ -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);
});
1 change: 1 addition & 0 deletions src/msg.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ table Truncate {
table Open {
filename: string;
perm: uint;
mode: string;
}

table OpenRes {
Expand Down
42 changes: 40 additions & 2 deletions src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit c2b91a3

Please sign in to comment.