mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Flesh out open() modes (#1282)
This commit is contained in:
parent
e31866a60d
commit
c2b91a3ef6
4 changed files with 151 additions and 6 deletions
31
js/files.ts
31
js/files.ts
|
@ -29,14 +29,37 @@ export const stdout = new File(1);
|
||||||
/** An instance of `File` for stderr. */
|
/** An instance of `File` for stderr. */
|
||||||
export const stderr = new File(2);
|
export const stderr = new File(2);
|
||||||
|
|
||||||
// TODO This is just a placeholder - not final API.
|
type OpenMode =
|
||||||
export type OpenMode = "r" | "w" | "w+" | "x";
|
/** 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
|
/** A factory function for creating instances of `File` associated with the
|
||||||
* supplied file name.
|
* supplied file name.
|
||||||
*/
|
*/
|
||||||
export function create(filename: string): Promise<File> {
|
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.
|
/** Open a file and return an instance of the `File` object.
|
||||||
|
@ -52,8 +75,10 @@ export async function open(
|
||||||
): Promise<File> {
|
): Promise<File> {
|
||||||
const builder = flatbuffers.createBuilder();
|
const builder = flatbuffers.createBuilder();
|
||||||
const filename_ = builder.createString(filename);
|
const filename_ = builder.createString(filename);
|
||||||
|
const mode_ = builder.createString(mode);
|
||||||
msg.Open.startOpen(builder);
|
msg.Open.startOpen(builder);
|
||||||
msg.Open.addFilename(builder, filename_);
|
msg.Open.addFilename(builder, filename_);
|
||||||
|
msg.Open.addMode(builder, mode_);
|
||||||
const inner = msg.Open.endOpen(builder);
|
const inner = msg.Open.endOpen(builder);
|
||||||
const baseRes = await dispatch.sendAsync(builder, msg.Any.Open, inner);
|
const baseRes = await dispatch.sendAsync(builder, msg.Any.Open, inner);
|
||||||
assert(baseRes != null);
|
assert(baseRes != null);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
import * as deno from "deno";
|
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() {
|
test(function filesStdioFileDescriptors() {
|
||||||
assertEqual(deno.stdin.rid, 0);
|
assertEqual(deno.stdin.rid, 0);
|
||||||
|
@ -29,3 +29,84 @@ test(async function filesToAsyncIterator() {
|
||||||
|
|
||||||
assertEqual(totalSize, 12);
|
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);
|
||||||
|
});
|
||||||
|
|
|
@ -348,6 +348,7 @@ table Truncate {
|
||||||
table Open {
|
table Open {
|
||||||
filename: string;
|
filename: string;
|
||||||
perm: uint;
|
perm: uint;
|
||||||
|
mode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
table OpenRes {
|
table OpenRes {
|
||||||
|
|
42
src/ops.rs
42
src/ops.rs
|
@ -584,9 +584,47 @@ fn op_open(
|
||||||
let cmd_id = base.cmd_id();
|
let cmd_id = base.cmd_id();
|
||||||
let inner = base.inner_as_open().unwrap();
|
let inner = base.inner_as_open().unwrap();
|
||||||
let filename = PathBuf::from(inner.filename().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)
|
.map_err(DenoError::from)
|
||||||
.and_then(move |fs_file| -> OpResult {
|
.and_then(move |fs_file| -> OpResult {
|
||||||
let resource = resources::add_fs_file(fs_file);
|
let resource = resources::add_fs_file(fs_file);
|
||||||
|
|
Loading…
Add table
Reference in a new issue