1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 08:39:09 -05:00

feat: Deno.makeTempFile (#4024)

This commit is contained in:
Kevin (Kun) "Kassimo" Qian 2020-02-18 11:45:59 -08:00 committed by GitHub
parent f0f807c524
commit 08dcf6bff7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 208 additions and 47 deletions

View file

@ -60,10 +60,11 @@ fn set_permissions(_file: &mut File, _perm: u32) -> std::io::Result<()> {
Ok(()) Ok(())
} }
pub fn make_temp_dir( pub fn make_temp(
dir: Option<&Path>, dir: Option<&Path>,
prefix: Option<&str>, prefix: Option<&str>,
suffix: Option<&str>, suffix: Option<&str>,
is_dir: bool,
) -> std::io::Result<PathBuf> { ) -> std::io::Result<PathBuf> {
let prefix_ = prefix.unwrap_or(""); let prefix_ = prefix.unwrap_or("");
let suffix_ = suffix.unwrap_or(""); let suffix_ = suffix.unwrap_or("");
@ -77,7 +78,15 @@ pub fn make_temp_dir(
let unique = rng.gen::<u32>(); let unique = rng.gen::<u32>();
buf.set_file_name(format!("{}{:08x}{}", prefix_, unique, suffix_)); buf.set_file_name(format!("{}{:08x}{}", prefix_, unique, suffix_));
// TODO: on posix, set mode flags to 0o700. // TODO: on posix, set mode flags to 0o700.
let r = create_dir(buf.as_path()); let r = if is_dir {
create_dir(buf.as_path())
} else {
OpenOptions::new()
.write(true)
.create_new(true)
.open(buf.as_path())
.map(|_| ())
};
match r { match r {
Err(ref e) if e.kind() == ErrorKind::AlreadyExists => continue, Err(ref e) if e.kind() == ErrorKind::AlreadyExists => continue,
Ok(_) => return Ok(buf), Ok(_) => return Ok(buf),

View file

@ -66,8 +66,10 @@ export { linkSync, link } from "./link.ts";
export { export {
makeTempDirSync, makeTempDirSync,
makeTempDir, makeTempDir,
MakeTempDirOptions makeTempFileSync,
} from "./make_temp_dir.ts"; makeTempFile,
MakeTempOptions
} from "./make_temp.ts";
export { metrics, Metrics } from "./metrics.ts"; export { metrics, Metrics } from "./metrics.ts";
export { mkdirSync, mkdir } from "./mkdir.ts"; export { mkdirSync, mkdir } from "./mkdir.ts";
export { export {

View file

@ -65,6 +65,7 @@ export let OP_SYMLINK: number;
export let OP_READ_LINK: number; export let OP_READ_LINK: number;
export let OP_TRUNCATE: number; export let OP_TRUNCATE: number;
export let OP_MAKE_TEMP_DIR: number; export let OP_MAKE_TEMP_DIR: number;
export let OP_MAKE_TEMP_FILE: number;
export let OP_CWD: number; export let OP_CWD: number;
export let OP_CONNECT_TLS: number; export let OP_CONNECT_TLS: number;
export let OP_HOSTNAME: number; export let OP_HOSTNAME: number;

View file

@ -680,9 +680,9 @@ declare namespace Deno {
mode?: number mode?: number
): Promise<void>; ): Promise<void>;
// @url js/make_temp_dir.d.ts // @url js/make_temp.d.ts
export interface MakeTempDirOptions { export interface MakeTempOptions {
dir?: string; dir?: string;
prefix?: string; prefix?: string;
suffix?: string; suffix?: string;
@ -696,7 +696,7 @@ declare namespace Deno {
* Requires allow-write. * Requires allow-write.
*/ */
// TODO(ry) Doesn't check permissions. // TODO(ry) Doesn't check permissions.
export function makeTempDirSync(options?: MakeTempDirOptions): string; export function makeTempDirSync(options?: MakeTempOptions): string;
/** makeTempDir creates a new temporary directory in the directory `dir`, its /** makeTempDir creates a new temporary directory in the directory `dir`, its
* name beginning with `prefix` and ending with `suffix`. * name beginning with `prefix` and ending with `suffix`.
@ -712,7 +712,27 @@ declare namespace Deno {
* Requires allow-write. * Requires allow-write.
*/ */
// TODO(ry) Doesn't check permissions. // TODO(ry) Doesn't check permissions.
export function makeTempDir(options?: MakeTempDirOptions): Promise<string>; export function makeTempDir(options?: MakeTempOptions): Promise<string>;
/** makeTempFileSync is the synchronous version of `makeTempFile`.
*
* const tempFileName0 = Deno.makeTempFileSync();
* const tempFileName1 = Deno.makeTempFileSync({ prefix: 'my_temp' });
*/
export function makeTempFileSync(options?: MakeTempOptions): string;
/** makeTempFile creates a new temporary file in the directory `dir`, its
* name beginning with `prefix` and ending with `suffix`.
* It returns the full path to the newly created file.
* If `dir` is unspecified, tempFile uses the default directory for temporary
* files. Multiple programs calling tempFile simultaneously will not choose the
* same directory. It is the caller's responsibility to remove the file
* when no longer needed.
*
* const tempFileName0 = await Deno.makeTempFile();
* const tempFileName1 = await Deno.makeTempFile({ prefix: 'my_temp' });
*/
export function makeTempFile(options?: MakeTempOptions): Promise<string>;
/** Changes the permission of a specific file/directory of specified path /** Changes the permission of a specific file/directory of specified path
* synchronously. * synchronously.

61
cli/js/make_temp.ts Normal file
View file

@ -0,0 +1,61 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { sendSync, sendAsync } from "./dispatch_json.ts";
import * as dispatch from "./dispatch.ts";
export interface MakeTempOptions {
dir?: string;
prefix?: string;
suffix?: string;
}
/** makeTempDirSync is the synchronous version of `makeTempDir`.
*
* const tempDirName0 = Deno.makeTempDirSync();
* const tempDirName1 = Deno.makeTempDirSync({ prefix: 'my_temp' });
*/
export function makeTempDirSync(options: MakeTempOptions = {}): string {
return sendSync(dispatch.OP_MAKE_TEMP_DIR, options);
}
/** makeTempDir 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.
*
* const tempDirName0 = await Deno.makeTempDir();
* const tempDirName1 = await Deno.makeTempDir({ prefix: 'my_temp' });
*/
export async function makeTempDir(
options: MakeTempOptions = {}
): Promise<string> {
return await sendAsync(dispatch.OP_MAKE_TEMP_DIR, options);
}
/** makeTempFileSync is the synchronous version of `makeTempFile`.
*
* const tempFileName0 = Deno.makeTempFileSync();
* const tempFileName1 = Deno.makeTempFileSync({ prefix: 'my_temp' });
*/
export function makeTempFileSync(options: MakeTempOptions = {}): string {
return sendSync(dispatch.OP_MAKE_TEMP_FILE, options);
}
/** makeTempFile creates a new temporary file in the directory `dir`, its
* name beginning with `prefix` and ending with `suffix`.
* It returns the full path to the newly created file.
* If `dir` is unspecified, tempFile uses the default directory for temporary
* files. Multiple programs calling tempFile simultaneously will not choose the
* same directory. It is the caller's responsibility to remove the file
* when no longer needed.
*
* const tempFileName0 = await Deno.makeTempFile();
* const tempFileName1 = await Deno.makeTempFile({ prefix: 'my_temp' });
*/
export async function makeTempFile(
options: MakeTempOptions = {}
): Promise<string> {
return await sendAsync(dispatch.OP_MAKE_TEMP_FILE, options);
}

View file

@ -1,35 +0,0 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { sendSync, sendAsync } from "./dispatch_json.ts";
import * as dispatch from "./dispatch.ts";
export interface MakeTempDirOptions {
dir?: string;
prefix?: string;
suffix?: string;
}
/** makeTempDirSync is the synchronous version of `makeTempDir`.
*
* const tempDirName0 = Deno.makeTempDirSync();
* const tempDirName1 = Deno.makeTempDirSync({ prefix: 'my_temp' });
*/
export function makeTempDirSync(options: MakeTempDirOptions = {}): string {
return sendSync(dispatch.OP_MAKE_TEMP_DIR, options);
}
/** makeTempDir 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.
*
* const tempDirName0 = await Deno.makeTempDir();
* const tempDirName1 = await Deno.makeTempDir({ prefix: 'my_temp' });
*/
export async function makeTempDir(
options: MakeTempDirOptions = {}
): Promise<string> {
return await sendAsync(dispatch.OP_MAKE_TEMP_DIR, options);
}

View file

@ -64,3 +64,69 @@ testPerm({ write: true }, async function makeTempDirSuccess(): Promise<void> {
assertEquals(err.kind, Deno.ErrorKind.NotFound); assertEquals(err.kind, Deno.ErrorKind.NotFound);
assertEquals(err.name, "NotFound"); assertEquals(err.name, "NotFound");
}); });
testPerm({ write: true }, function makeTempFileSyncSuccess(): void {
const file1 = Deno.makeTempFileSync({ prefix: "hello", suffix: "world" });
const file2 = Deno.makeTempFileSync({ prefix: "hello", suffix: "world" });
// Check that both dirs are different.
assert(file1 !== file2);
for (const dir of [file1, file2]) {
// 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 dir = Deno.makeTempDirSync({ prefix: "tempdir" });
const file3 = Deno.makeTempFileSync({ dir });
assert(file3.startsWith(dir));
assert(/^[\\\/]/.test(file3.slice(dir.length)));
// Check that creating a temp file inside a nonexisting directory fails.
let err;
try {
Deno.makeTempFileSync({ dir: "/baddir" });
} catch (err_) {
err = err_;
}
assertEquals(err.kind, Deno.ErrorKind.NotFound);
assertEquals(err.name, "NotFound");
});
test(function makeTempFileSyncPerm(): void {
// makeTempFileSync should require write permissions (for now).
let err;
try {
Deno.makeTempFileSync({ dir: "/baddir" });
} catch (err_) {
err = err_;
}
assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
assertEquals(err.name, "PermissionDenied");
});
testPerm({ write: true }, async function makeTempFileSuccess(): Promise<void> {
const file1 = await Deno.makeTempFile({ prefix: "hello", suffix: "world" });
const file2 = await Deno.makeTempFile({ prefix: "hello", suffix: "world" });
// Check that both dirs are different.
assert(file1 !== file2);
for (const dir of [file1, file2]) {
// 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 dir = Deno.makeTempDirSync({ prefix: "tempdir" });
const file3 = await Deno.makeTempFile({ dir });
assert(file3.startsWith(dir));
assert(/^[\\\/]/.test(file3.slice(dir.length)));
// Check that creating a temp file inside a nonexisting directory fails.
let err;
try {
await Deno.makeTempFile({ dir: "/baddir" });
} catch (err_) {
err = err_;
}
assertEquals(err.kind, Deno.ErrorKind.NotFound);
assertEquals(err.name, "NotFound");
});

View file

@ -29,7 +29,7 @@ import "./headers_test.ts";
import "./internals_test.ts"; import "./internals_test.ts";
import "./link_test.ts"; import "./link_test.ts";
import "./location_test.ts"; import "./location_test.ts";
import "./make_temp_dir_test.ts"; import "./make_temp_test.ts";
import "./metrics_test.ts"; import "./metrics_test.ts";
import "./mixins/dom_iterable_test.ts"; import "./mixins/dom_iterable_test.ts";
import "./mkdir_test.ts"; import "./mkdir_test.ts";

View file

@ -38,6 +38,10 @@ pub fn init(i: &mut Isolate, s: &State) {
"make_temp_dir", "make_temp_dir",
s.core_op(json_op(s.stateful_op(op_make_temp_dir))), s.core_op(json_op(s.stateful_op(op_make_temp_dir))),
); );
i.register_op(
"make_temp_file",
s.core_op(json_op(s.stateful_op(op_make_temp_file))),
);
i.register_op("cwd", s.core_op(json_op(s.stateful_op(op_cwd)))); i.register_op("cwd", s.core_op(json_op(s.stateful_op(op_cwd))));
i.register_op("utime", s.core_op(json_op(s.stateful_op(op_utime)))); i.register_op("utime", s.core_op(json_op(s.stateful_op(op_utime))));
} }
@ -529,7 +533,7 @@ fn op_truncate(
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct MakeTempDirArgs { struct MakeTempArgs {
promise_id: Option<u64>, promise_id: Option<u64>,
dir: Option<String>, dir: Option<String>,
prefix: Option<String>, prefix: Option<String>,
@ -541,7 +545,7 @@ fn op_make_temp_dir(
args: Value, args: Value,
_zero_copy: Option<ZeroCopyBuf>, _zero_copy: Option<ZeroCopyBuf>,
) -> Result<JsonOp, ErrBox> { ) -> Result<JsonOp, ErrBox> {
let args: MakeTempDirArgs = serde_json::from_value(args)?; let args: MakeTempArgs = serde_json::from_value(args)?;
let dir = args.dir.map(PathBuf::from); let dir = args.dir.map(PathBuf::from);
let prefix = args.prefix.map(String::from); let prefix = args.prefix.map(String::from);
@ -555,11 +559,44 @@ fn op_make_temp_dir(
// TODO(piscisaureus): use byte vector for paths, not a string. // TODO(piscisaureus): use byte vector for paths, not a string.
// See https://github.com/denoland/deno/issues/627. // See https://github.com/denoland/deno/issues/627.
// We can't assume that paths are always valid utf8 strings. // We can't assume that paths are always valid utf8 strings.
let path = deno_fs::make_temp_dir( let path = deno_fs::make_temp(
// Converting Option<String> to Option<&str> // Converting Option<String> to Option<&str>
dir.as_ref().map(|x| &**x), dir.as_ref().map(|x| &**x),
prefix.as_ref().map(|x| &**x), prefix.as_ref().map(|x| &**x),
suffix.as_ref().map(|x| &**x), suffix.as_ref().map(|x| &**x),
true,
)?;
let path_str = path.to_str().unwrap();
Ok(json!(path_str))
})
}
fn op_make_temp_file(
state: &State,
args: Value,
_zero_copy: Option<ZeroCopyBuf>,
) -> Result<JsonOp, ErrBox> {
let args: MakeTempArgs = serde_json::from_value(args)?;
let dir = args.dir.map(PathBuf::from);
let prefix = args.prefix.map(String::from);
let suffix = args.suffix.map(String::from);
state
.check_write(dir.clone().unwrap_or_else(std::env::temp_dir).as_path())?;
let is_sync = args.promise_id.is_none();
blocking_json(is_sync, move || {
// 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(
// Converting Option<String> to Option<&str>
dir.as_ref().map(|x| &**x),
prefix.as_ref().map(|x| &**x),
suffix.as_ref().map(|x| &**x),
false,
)?; )?;
let path_str = path.to_str().unwrap(); let path_str = path.to_str().unwrap();