mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 16:19:12 -05:00
feat: Deno.makeTempFile (#4024)
This commit is contained in:
parent
f0f807c524
commit
08dcf6bff7
9 changed files with 208 additions and 47 deletions
13
cli/fs.rs
13
cli/fs.rs
|
@ -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),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
28
cli/js/lib.deno.ns.d.ts
vendored
28
cli/js/lib.deno.ns.d.ts
vendored
|
@ -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
61
cli/js/make_temp.ts
Normal 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);
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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");
|
||||||
|
});
|
|
@ -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";
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue