Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Implementation of create file #1282

Merged
merged 9 commits into from
Dec 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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> {
bartlomieju marked this conversation as resolved.
Show resolved Hide resolved
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);
});
bartlomieju marked this conversation as resolved.
Show resolved Hide resolved

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();

bartlomieju marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -349,6 +349,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 @@ -588,9 +588,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.");
}
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I move this whole operation somewhere else?

Probably some comments explaining why each option is used would be appropriate as well.

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