mirror of
https://github.com/denoland/deno.git
synced 2024-11-29 16:30:56 -05:00
Move writeFileSync to write_file.ts, add writeFile and tests (#728)
This commit is contained in:
parent
c2663e1d82
commit
05f87a0cf2
10 changed files with 192 additions and 83 deletions
|
@ -8,11 +8,11 @@ export {
|
||||||
makeTempDirSync,
|
makeTempDirSync,
|
||||||
renameSync,
|
renameSync,
|
||||||
statSync,
|
statSync,
|
||||||
lstatSync,
|
lstatSync
|
||||||
writeFileSync
|
|
||||||
} from "./os";
|
} from "./os";
|
||||||
export { mkdirSync, mkdir } from "./mkdir";
|
export { mkdirSync, mkdir } from "./mkdir";
|
||||||
export { readFileSync, readFile } from "./read_file";
|
export { readFileSync, readFile } from "./read_file";
|
||||||
|
export { writeFileSync, writeFile } from "./write_file";
|
||||||
export { ErrorKind, DenoError } from "./errors";
|
export { ErrorKind, DenoError } from "./errors";
|
||||||
export { libdeno } from "./libdeno";
|
export { libdeno } from "./libdeno";
|
||||||
export const argv: string[] = [];
|
export const argv: string[] = [];
|
||||||
|
|
32
js/os.ts
32
js/os.ts
|
@ -274,38 +274,6 @@ function statSyncInner(filename: string, lstat: boolean): FileInfo {
|
||||||
return new FileInfo(res);
|
return new FileInfo(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a new file.
|
|
||||||
* import { writeFileSync } from "deno";
|
|
||||||
*
|
|
||||||
* const encoder = new TextEncoder("utf-8");
|
|
||||||
* const data = encoder.encode("Hello world\n");
|
|
||||||
* writeFileSync("hello.txt", data);
|
|
||||||
*/
|
|
||||||
export function writeFileSync(
|
|
||||||
filename: string,
|
|
||||||
data: Uint8Array,
|
|
||||||
perm = 0o666
|
|
||||||
): void {
|
|
||||||
/* Ideally we could write:
|
|
||||||
const res = sendSync({
|
|
||||||
command: fbs.Command.WRITE_FILE_SYNC,
|
|
||||||
writeFileSyncFilename: filename,
|
|
||||||
writeFileSyncData: data,
|
|
||||||
writeFileSyncPerm: perm
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
const builder = new flatbuffers.Builder();
|
|
||||||
const filename_ = builder.createString(filename);
|
|
||||||
const dataOffset = fbs.WriteFileSync.createDataVector(builder, data);
|
|
||||||
fbs.WriteFileSync.startWriteFileSync(builder);
|
|
||||||
fbs.WriteFileSync.addFilename(builder, filename_);
|
|
||||||
fbs.WriteFileSync.addData(builder, dataOffset);
|
|
||||||
fbs.WriteFileSync.addPerm(builder, perm);
|
|
||||||
const msg = fbs.WriteFileSync.endWriteFileSync(builder);
|
|
||||||
sendSync(builder, fbs.Any.WriteFileSync, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames (moves) oldpath to newpath.
|
* Renames (moves) oldpath to newpath.
|
||||||
* import { renameSync } from "deno";
|
* import { renameSync } from "deno";
|
||||||
|
|
|
@ -85,35 +85,6 @@ test(async function lstatSyncNotFound() {
|
||||||
assertEqual(badInfo, undefined);
|
assertEqual(badInfo, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
testPerm({ write: true }, function writeFileSyncSuccess() {
|
|
||||||
const enc = new TextEncoder();
|
|
||||||
const data = enc.encode("Hello");
|
|
||||||
const filename = deno.makeTempDirSync() + "/test.txt";
|
|
||||||
deno.writeFileSync(filename, data, 0o666);
|
|
||||||
const dataRead = deno.readFileSync(filename);
|
|
||||||
const dec = new TextDecoder("utf-8");
|
|
||||||
const actual = dec.decode(dataRead);
|
|
||||||
assertEqual("Hello", actual);
|
|
||||||
});
|
|
||||||
|
|
||||||
// For this test to pass we need --allow-write permission.
|
|
||||||
// Otherwise it will fail with deno.PermissionDenied instead of deno.NotFound.
|
|
||||||
testPerm({ write: true }, function writeFileSyncFail() {
|
|
||||||
const enc = new TextEncoder();
|
|
||||||
const data = enc.encode("Hello");
|
|
||||||
const filename = "/baddir/test.txt";
|
|
||||||
// The following should fail because /baddir doesn't exist (hopefully).
|
|
||||||
let caughtError = false;
|
|
||||||
try {
|
|
||||||
deno.writeFileSync(filename, data);
|
|
||||||
} catch (e) {
|
|
||||||
caughtError = true;
|
|
||||||
assertEqual(e.kind, deno.ErrorKind.NotFound);
|
|
||||||
assertEqual(e.name, "NotFound");
|
|
||||||
}
|
|
||||||
assert(caughtError);
|
|
||||||
});
|
|
||||||
|
|
||||||
testPerm({ write: true }, function makeTempDirSync() {
|
testPerm({ write: true }, function makeTempDirSync() {
|
||||||
const dir1 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
|
const dir1 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
|
||||||
const dir2 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
|
const dir2 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
|
||||||
|
|
|
@ -6,4 +6,5 @@ import "./console_test.ts";
|
||||||
import "./fetch_test.ts";
|
import "./fetch_test.ts";
|
||||||
import "./os_test.ts";
|
import "./os_test.ts";
|
||||||
import "./read_file_test.ts";
|
import "./read_file_test.ts";
|
||||||
|
import "./write_file_test.ts";
|
||||||
import "./mkdir_test.ts";
|
import "./mkdir_test.ts";
|
||||||
|
|
54
js/write_file.ts
Normal file
54
js/write_file.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a new file, with given filename and data synchronously.
|
||||||
|
*
|
||||||
|
* import { writeFileSync } from "deno";
|
||||||
|
*
|
||||||
|
* const encoder = new TextEncoder("utf-8");
|
||||||
|
* const data = encoder.encode("Hello world\n");
|
||||||
|
* writeFileSync("hello.txt", data);
|
||||||
|
*/
|
||||||
|
export function writeFileSync(
|
||||||
|
filename: string,
|
||||||
|
data: Uint8Array,
|
||||||
|
perm = 0o666
|
||||||
|
): void {
|
||||||
|
dispatch.sendSync(...req(filename, data, perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a new file, with given filename and data.
|
||||||
|
*
|
||||||
|
* import { writeFile } from "deno";
|
||||||
|
*
|
||||||
|
* const encoder = new TextEncoder("utf-8");
|
||||||
|
* const data = encoder.encode("Hello world\n");
|
||||||
|
* await writeFile("hello.txt", data);
|
||||||
|
*/
|
||||||
|
export async function writeFile(
|
||||||
|
filename: string,
|
||||||
|
data: Uint8Array,
|
||||||
|
perm = 0o666
|
||||||
|
): Promise<void> {
|
||||||
|
await dispatch.sendAsync(...req(filename, data, perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
function req(
|
||||||
|
filename: string,
|
||||||
|
data: Uint8Array,
|
||||||
|
perm: number
|
||||||
|
): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] {
|
||||||
|
const builder = new flatbuffers.Builder();
|
||||||
|
const filename_ = builder.createString(filename);
|
||||||
|
const dataOffset = fbs.WriteFile.createDataVector(builder, data);
|
||||||
|
fbs.WriteFile.startWriteFile(builder);
|
||||||
|
fbs.WriteFile.addFilename(builder, filename_);
|
||||||
|
fbs.WriteFile.addData(builder, dataOffset);
|
||||||
|
fbs.WriteFile.addPerm(builder, perm);
|
||||||
|
const msg = fbs.WriteFile.endWriteFile(builder);
|
||||||
|
return [builder, fbs.Any.WriteFile, msg];
|
||||||
|
}
|
89
js/write_file_test.ts
Normal file
89
js/write_file_test.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { testPerm, assert, assertEqual } from "./test_util.ts";
|
||||||
|
import * as deno from "deno";
|
||||||
|
|
||||||
|
testPerm({ write: true }, function writeFileSyncSuccess() {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const filename = deno.makeTempDirSync() + "/test.txt";
|
||||||
|
deno.writeFileSync(filename, data, 0o666);
|
||||||
|
const dataRead = deno.readFileSync(filename);
|
||||||
|
const dec = new TextDecoder("utf-8");
|
||||||
|
const actual = dec.decode(dataRead);
|
||||||
|
assertEqual("Hello", actual);
|
||||||
|
});
|
||||||
|
|
||||||
|
testPerm({ write: true }, function writeFileSyncFail() {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const filename = "/baddir/test.txt";
|
||||||
|
// The following should fail because /baddir doesn't exist (hopefully).
|
||||||
|
let caughtError = false;
|
||||||
|
try {
|
||||||
|
deno.writeFileSync(filename, data);
|
||||||
|
} catch (e) {
|
||||||
|
caughtError = true;
|
||||||
|
assertEqual(e.kind, deno.ErrorKind.NotFound);
|
||||||
|
assertEqual(e.name, "NotFound");
|
||||||
|
}
|
||||||
|
assert(caughtError);
|
||||||
|
});
|
||||||
|
|
||||||
|
testPerm({ write: false }, function writeFileSyncPerm() {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const filename = "/baddir/test.txt";
|
||||||
|
// The following should fail due to no write permission
|
||||||
|
let caughtError = false;
|
||||||
|
try {
|
||||||
|
deno.writeFileSync(filename, data);
|
||||||
|
} catch (e) {
|
||||||
|
caughtError = true;
|
||||||
|
assertEqual(e.kind, deno.ErrorKind.PermissionDenied);
|
||||||
|
assertEqual(e.name, "PermissionDenied");
|
||||||
|
}
|
||||||
|
assert(caughtError);
|
||||||
|
});
|
||||||
|
|
||||||
|
testPerm({ write: true }, async function writeFileSuccess() {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const filename = deno.makeTempDirSync() + "/test.txt";
|
||||||
|
await deno.writeFile(filename, data, 0o666);
|
||||||
|
const dataRead = deno.readFileSync(filename);
|
||||||
|
const dec = new TextDecoder("utf-8");
|
||||||
|
const actual = dec.decode(dataRead);
|
||||||
|
assertEqual("Hello", actual);
|
||||||
|
});
|
||||||
|
|
||||||
|
testPerm({ write: true }, async function writeFileNotFound() {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const filename = "/baddir/test.txt";
|
||||||
|
// The following should fail because /baddir doesn't exist (hopefully).
|
||||||
|
let caughtError = false;
|
||||||
|
try {
|
||||||
|
await deno.writeFile(filename, data);
|
||||||
|
} catch (e) {
|
||||||
|
caughtError = true;
|
||||||
|
assertEqual(e.kind, deno.ErrorKind.NotFound);
|
||||||
|
assertEqual(e.name, "NotFound");
|
||||||
|
}
|
||||||
|
assert(caughtError);
|
||||||
|
});
|
||||||
|
|
||||||
|
testPerm({ write: false }, async function writeFilePerm() {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const filename = "/baddir/test.txt";
|
||||||
|
// The following should fail due to no write permission
|
||||||
|
let caughtError = false;
|
||||||
|
try {
|
||||||
|
await deno.writeFile(filename, data);
|
||||||
|
} catch (e) {
|
||||||
|
caughtError = true;
|
||||||
|
assertEqual(e.kind, deno.ErrorKind.PermissionDenied);
|
||||||
|
assertEqual(e.name, "PermissionDenied");
|
||||||
|
}
|
||||||
|
assert(caughtError);
|
||||||
|
});
|
|
@ -118,7 +118,7 @@ impl DenoDir {
|
||||||
Some(ref parent) => fs::create_dir_all(parent),
|
Some(ref parent) => fs::create_dir_all(parent),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}?;
|
}?;
|
||||||
deno_fs::write_file_sync(&p, source.as_bytes())?;
|
deno_fs::write_file(&p, source.as_bytes(), 0o666)?;
|
||||||
source
|
source
|
||||||
} else {
|
} else {
|
||||||
let source = fs::read_to_string(&p)?;
|
let source = fs::read_to_string(&p)?;
|
||||||
|
|
35
src/fs.rs
35
src/fs.rs
|
@ -1,5 +1,5 @@
|
||||||
use std;
|
use std;
|
||||||
use std::fs::{create_dir, File};
|
use std::fs::{create_dir, File, OpenOptions};
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -7,9 +7,36 @@ use std::path::{Path, PathBuf};
|
||||||
use rand;
|
use rand;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
pub fn write_file_sync(path: &Path, content: &[u8]) -> std::io::Result<()> {
|
#[cfg(any(unix))]
|
||||||
let mut f = File::create(path)?;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
f.write_all(content)
|
|
||||||
|
pub fn write_file(
|
||||||
|
filename: &Path,
|
||||||
|
data: &[u8],
|
||||||
|
perm: u32,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
let is_append = perm & (1 << 31) != 0;
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(false)
|
||||||
|
.write(true)
|
||||||
|
.append(is_append)
|
||||||
|
.truncate(!is_append)
|
||||||
|
.create(true)
|
||||||
|
.open(filename)?;
|
||||||
|
|
||||||
|
set_permissions(&mut file, perm)?;
|
||||||
|
file.write_all(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(unix))]
|
||||||
|
fn set_permissions(file: &mut File, perm: u32) -> std::io::Result<()> {
|
||||||
|
debug!("set file perm to {}", perm);
|
||||||
|
file.set_permissions(PermissionsExt::from_mode(perm & 0o777))
|
||||||
|
}
|
||||||
|
#[cfg(not(any(unix)))]
|
||||||
|
fn set_permissions(_file: &mut File, _perm: u32) -> std::io::Result<()> {
|
||||||
|
// NOOP on windows
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_temp_dir(
|
pub fn make_temp_dir(
|
||||||
|
|
|
@ -54,7 +54,7 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
|
||||||
msg::Any::RenameSync => handle_rename_sync,
|
msg::Any::RenameSync => handle_rename_sync,
|
||||||
msg::Any::SetEnv => handle_set_env,
|
msg::Any::SetEnv => handle_set_env,
|
||||||
msg::Any::StatSync => handle_stat_sync,
|
msg::Any::StatSync => handle_stat_sync,
|
||||||
msg::Any::WriteFileSync => handle_write_file_sync,
|
msg::Any::WriteFile => handle_write_file,
|
||||||
msg::Any::Exit => handle_exit,
|
msg::Any::Exit => handle_exit,
|
||||||
_ => panic!(format!(
|
_ => panic!(format!(
|
||||||
"Unhandled message {}",
|
"Unhandled message {}",
|
||||||
|
@ -517,20 +517,19 @@ fn handle_stat_sync(_d: *const DenoC, base: &msg::Base) -> Box<Op> {
|
||||||
}()))
|
}()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_write_file_sync(d: *const DenoC, base: &msg::Base) -> Box<Op> {
|
fn handle_write_file(d: *const DenoC, base: &msg::Base) -> Box<Op> {
|
||||||
let msg = base.msg_as_write_file_sync().unwrap();
|
let msg = base.msg_as_write_file().unwrap();
|
||||||
let filename = String::from(msg.filename().unwrap());
|
let filename = String::from(msg.filename().unwrap());
|
||||||
let data = msg.data().unwrap();
|
let data = msg.data().unwrap();
|
||||||
// TODO let perm = msg.perm();
|
let perm = msg.perm();
|
||||||
let deno = from_c(d);
|
let deno = from_c(d);
|
||||||
|
|
||||||
debug!("handle_write_file_sync {}", filename);
|
debug!("handle_write_file {}", filename);
|
||||||
Box::new(futures::future::result(|| -> OpResult {
|
Box::new(futures::future::result(|| -> OpResult {
|
||||||
if !deno.flags.allow_write {
|
if !deno.flags.allow_write {
|
||||||
Err(permission_denied())
|
Err(permission_denied())
|
||||||
} else {
|
} else {
|
||||||
// TODO(ry) Use perm.
|
deno_fs::write_file(Path::new(&filename), data, perm)?;
|
||||||
deno_fs::write_file_sync(Path::new(&filename), data)?;
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}()))
|
}()))
|
||||||
|
|
16
src/msg.fbs
16
src/msg.fbs
|
@ -17,11 +17,11 @@ union Any {
|
||||||
Mkdir,
|
Mkdir,
|
||||||
ReadFile,
|
ReadFile,
|
||||||
ReadFileRes,
|
ReadFileRes,
|
||||||
|
WriteFile,
|
||||||
RenameSync,
|
RenameSync,
|
||||||
StatSync,
|
StatSync,
|
||||||
StatSyncRes,
|
StatSyncRes,
|
||||||
SetEnv,
|
SetEnv,
|
||||||
WriteFileSync,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ErrorKind: byte {
|
enum ErrorKind: byte {
|
||||||
|
@ -181,6 +181,13 @@ table ReadFileRes {
|
||||||
data: [ubyte];
|
data: [ubyte];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table WriteFile {
|
||||||
|
filename: string;
|
||||||
|
data: [ubyte];
|
||||||
|
perm: uint;
|
||||||
|
// perm specified by https://godoc.org/os#FileMode
|
||||||
|
}
|
||||||
|
|
||||||
table RenameSync {
|
table RenameSync {
|
||||||
oldpath: string;
|
oldpath: string;
|
||||||
newpath: string;
|
newpath: string;
|
||||||
|
@ -200,11 +207,4 @@ table StatSyncRes {
|
||||||
created:ulong;
|
created:ulong;
|
||||||
}
|
}
|
||||||
|
|
||||||
table WriteFileSync {
|
|
||||||
filename: string;
|
|
||||||
data: [ubyte];
|
|
||||||
perm: uint;
|
|
||||||
// perm specified by https://godoc.org/os#FileMode
|
|
||||||
}
|
|
||||||
|
|
||||||
root_type Base;
|
root_type Base;
|
||||||
|
|
Loading…
Reference in a new issue