1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

Implement makeTempDirSync()

This commit is contained in:
Bert Belder 2018-08-24 00:36:45 +02:00
parent a836c493f3
commit ceaf822682
No known key found for this signature in database
GPG key ID: 7A77887B2E2ED461
9 changed files with 188 additions and 25 deletions

View file

@ -43,6 +43,7 @@ main_extern = [
"$rust_build:log",
"$rust_build:sha1",
"$rust_build:tempfile",
"$rust_build:rand",
"$rust_build:tokio",
"$rust_build:tokio_current_thread",
"$rust_build:url",

View file

@ -11,6 +11,7 @@ version = "0.0.0"
url = "1.7.1"
libc = "0.2.42"
log = "0.4.3"
rand = "0.5.4"
sha1 = "0.6.0"
tempfile = "3"
tokio = {git = "https://github.com/tokio-rs/tokio.git", rev = "5d0d2a2e1214f856993df6965825c89bfcaa879e"}

View file

@ -1,5 +1,5 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
// Public deno module.
export { exit, readFileSync, writeFileSync } from "./os";
export { exit, makeTempDirSync, readFileSync, writeFileSync } from "./os";
export { libdeno } from "./libdeno";
export const argv: string[] = [];

View file

@ -85,6 +85,57 @@ export function codeCache(
}
}
/**
* makeTempDirSync creates a new temporary directory in the directory `dir`, its
* name beginning with `prefix` and ending with `suffix`.
* It returns the full path to the newly created directory.
* If `dir` is unspecified, tempDir uses the default directory for temporary
* files. Multiple programs calling tempDir simultaneously will not choose the
* same directory. It is the caller's responsibility to remove the directory
* when no longer needed.
*/
export interface MakeTempDirOptions {
dir?: string;
prefix?: string;
suffix?: string;
}
export function makeTempDirSync({
dir,
prefix,
suffix
}: MakeTempDirOptions = {}): string {
const builder = new flatbuffers.Builder();
const fbDir = dir == null ? -1 : builder.createString(dir);
const fbPrefix = prefix == null ? -1 : builder.createString(prefix);
const fbSuffix = suffix == null ? -1 : builder.createString(suffix);
fbs.MakeTempDir.startMakeTempDir(builder);
if (dir != null) {
fbs.MakeTempDir.addDir(builder, fbDir);
}
if (prefix != null) {
fbs.MakeTempDir.addPrefix(builder, fbPrefix);
}
if (suffix != null) {
fbs.MakeTempDir.addSuffix(builder, fbSuffix);
}
const msg = fbs.MakeTempDir.endMakeTempDir(builder);
fbs.Base.startBase(builder);
fbs.Base.addMsg(builder, msg);
fbs.Base.addMsgType(builder, fbs.Any.MakeTempDir);
builder.finish(fbs.Base.endBase(builder));
const resBuf = libdeno.send(builder.asUint8Array());
assert(resBuf != null);
const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf!));
const baseRes = fbs.Base.getRootAsBase(bb);
maybeThrowError(baseRes);
assert(fbs.Any.MakeTempDirRes === baseRes.msgType());
const res = new fbs.MakeTempDirRes();
assert(baseRes.msg(res) != null);
const path = res.path();
assert(path != null);
return path!;
}
export function readFileSync(filename: string): Uint8Array {
/* Ideally we could write
const res = send({

View file

@ -41,16 +41,16 @@ test(function tests_readFileSync_NotFound() {
});
*/
/* TODO(ry) Add this once we can create a tmpDir to write the file into.
test(function writeFileSyncSuccess() {
testPerm({ write: true }, function writeFileSync() {
const enc = new TextEncoder();
const dataWritten = enc.encode("Hello");
const filename = "TEMPDIR/test.txt";
deno.writeFileSync(filename, dataWritten, 0o666);
const data = enc.encode("Hello");
const filename = deno.makeTempDirSync() + "/test.txt";
deno.writeFileSync(filename, data, 0o666);
const dataRead = deno.readFileSync(filename);
assertEqual(dataRead, dataWritten);
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.
@ -70,23 +70,48 @@ testPerm({ write: true }, function writeFileSyncFail() {
assert(caughtError);
});
testPerm({ write: true }, function makeTempDirSync() {
const dir1 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
const dir2 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
// Check that both dirs are different.
assert(dir1 != dir2);
for (const dir of [dir1, dir2]) {
// Check that the prefix and suffix are applied.
const lastPart = dir.replace(/^.*[\\\/]/, "");
assert(lastPart.startsWith("hello"));
assert(lastPart.endsWith("world"));
}
// Check that the `dir` option works.
const dir3 = deno.makeTempDirSync({ dir: dir1 });
assert(dir3.startsWith(dir1));
assert(/^[\\\/]/.test(dir3.slice(dir1.length)));
// Check that creating a temp dir inside a nonexisting directory fails.
let err;
try {
deno.makeTempDirSync({ dir: "/baddir" });
} catch (err_) {
err = err_;
}
// TODO assert(err instanceof deno.NotFound).
assert(err);
assertEqual(err.name, "deno.NotFound");
});
test(function makeTempDirSyncPerm() {
// makeTempDirSync should require write permissions (for now).
let err;
try {
deno.makeTempDirSync({ dir: "/baddir" });
} catch (err_) {
err = err_;
}
// TODO assert(err instanceof deno.PermissionDenied).
assert(err);
assertEqual(err.name, "deno.PermissionDenied");
});
testPerm({ net: true }, async function tests_fetch() {
const response = await fetch("http://localhost:4545/package.json");
const json = await response.json();
assertEqual(json.name, "deno");
});
/*
test(async function tests_writeFileSync() {
const enc = new TextEncoder();
const data = enc.encode("Hello");
// TODO need ability to get tmp dir.
const fn = "/tmp/test.txt";
writeFileSync("/tmp/test.txt", data, 0o666);
const dataRead = deno.readFileSync("/tmp/test.txt");
const dec = new TextDecoder("utf-8");
const actual = dec.decode(dataRead);
assertEqual("Hello", actual);
});
*/

View file

@ -1,13 +1,41 @@
use std;
use std::fs::File;
use std::fs::{create_dir, File};
use std::io::ErrorKind;
use std::io::Write;
use std::path::Path;
use std::path::{Path, PathBuf};
use rand;
use rand::RngCore;
pub fn write_file_sync(path: &Path, content: &[u8]) -> std::io::Result<()> {
let mut f = File::create(path)?;
f.write_all(content)
}
pub fn make_temp_dir(
dir: Option<&Path>,
prefix: Option<&str>,
suffix: Option<&str>,
) -> std::io::Result<PathBuf> {
let prefix_ = prefix.unwrap_or("");
let suffix_ = suffix.unwrap_or("");
let mut buf: PathBuf = match dir {
Some(ref p) => p.to_path_buf(),
None => std::env::temp_dir(),
}.join("_");
loop {
let unique = rand::thread_rng().next_u32();
buf.set_file_name(format!("{}{:08x}{}", prefix_, unique, suffix_));
// TODO: on posix, set mode flags to 0o700.
let r = create_dir(buf.as_path());
match r {
Err(ref e) if e.kind() == ErrorKind::AlreadyExists => continue,
Ok(_) => return Ok(buf),
Err(e) => return Err(e),
}
}
}
pub fn mkdir(path: &Path) -> std::io::Result<()> {
debug!("mkdir -p {}", path.display());
assert!(path.has_root(), "non-has_root not yet implemented");

View file

@ -65,6 +65,13 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
let msg = msg::Exit::init_from_table(base.msg().unwrap());
std::process::exit(msg.code())
}
msg::Any::MakeTempDir => {
let msg = msg::MakeTempDir::init_from_table(base.msg().unwrap());
let dir = msg.dir();
let prefix = msg.prefix();
let suffix = msg.suffix();
handle_make_temp_dir(d, &mut builder, dir, prefix, suffix)
}
msg::Any::ReadFileSync => {
// TODO base.msg_as_ReadFileSync();
let msg = msg::ReadFileSync::init_from_table(base.msg().unwrap());
@ -395,6 +402,43 @@ fn send_timer_ready(d: *const DenoC, timer_id: u32, done: bool) {
);
}
fn handle_make_temp_dir(
d: *const DenoC,
builder: &mut FlatBufferBuilder,
dir: Option<&str>,
prefix: Option<&str>,
suffix: Option<&str>,
) -> HandlerResult {
let deno = from_c(d);
if !deno.flags.allow_write {
let err = std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"allow_write is off.",
);
return Err(err.into());
}
// TODO(piscisaureus): use byte vector for paths, not a string.
// See https://github.com/denoland/deno/issues/627.
// We can't assume that paths are always valid utf8 strings.
let path = deno_fs::make_temp_dir(dir.map(Path::new), prefix, suffix)?;
let path_off = builder.create_string(path.to_str().unwrap());
let msg = msg::MakeTempDirRes::create(
builder,
&msg::MakeTempDirResArgs {
path: Some(path_off),
..Default::default()
},
);
Ok(create_msg(
builder,
&msg::BaseArgs {
msg: Some(flatbuffers::Offset::new(msg.value())),
msg_type: msg::Any::MakeTempDirRes,
..Default::default()
},
))
}
// Prototype https://github.com/denoland/deno/blob/golang/os.go#L171-L184
fn handle_read_file_sync(
_d: *const DenoC,

View file

@ -3,6 +3,7 @@ extern crate futures;
extern crate hyper;
extern crate libc;
extern crate msg_rs as msg_generated;
extern crate rand;
extern crate sha1;
extern crate tempfile;
extern crate tokio;

View file

@ -12,6 +12,8 @@ union Any {
TimerClear,
FetchReq,
FetchRes,
MakeTempDir,
MakeTempDirRes,
ReadFileSync,
ReadFileSyncRes,
WriteFileSync,
@ -133,6 +135,16 @@ table FetchRes {
body: [ubyte];
}
table MakeTempDir {
dir: string;
prefix: string;
suffix: string;
}
table MakeTempDirRes {
path: string;
}
table ReadFileSync {
filename: string;
}