mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Implemented readDirSync, readDir
This commit is contained in:
parent
4c0517c339
commit
ea87034e26
9 changed files with 294 additions and 93 deletions
2
BUILD.gn
2
BUILD.gn
|
@ -75,6 +75,7 @@ ts_sources = [
|
||||||
"js/dom_types.ts",
|
"js/dom_types.ts",
|
||||||
"js/errors.ts",
|
"js/errors.ts",
|
||||||
"js/fetch.ts",
|
"js/fetch.ts",
|
||||||
|
"js/fileinfo.ts",
|
||||||
"js/files.ts",
|
"js/files.ts",
|
||||||
"js/io.ts",
|
"js/io.ts",
|
||||||
"js/global-eval.ts",
|
"js/global-eval.ts",
|
||||||
|
@ -88,6 +89,7 @@ ts_sources = [
|
||||||
"js/platform.ts",
|
"js/platform.ts",
|
||||||
"js/plugins.d.ts",
|
"js/plugins.d.ts",
|
||||||
"js/read_file.ts",
|
"js/read_file.ts",
|
||||||
|
"js/read_dir.ts",
|
||||||
"js/remove.ts",
|
"js/remove.ts",
|
||||||
"js/rename.ts",
|
"js/rename.ts",
|
||||||
"js/read_link.ts",
|
"js/read_link.ts",
|
||||||
|
|
|
@ -9,9 +9,10 @@ export { makeTempDirSync, makeTempDir } from "./make_temp_dir";
|
||||||
export { removeSync, remove, removeAllSync, removeAll } from "./remove";
|
export { removeSync, remove, removeAllSync, removeAll } from "./remove";
|
||||||
export { renameSync, rename } from "./rename";
|
export { renameSync, rename } from "./rename";
|
||||||
export { readFileSync, readFile } from "./read_file";
|
export { readFileSync, readFile } from "./read_file";
|
||||||
|
export { readDirSync, readDir } from "./read_dir";
|
||||||
export { copyFileSync, copyFile } from "./copy_file";
|
export { copyFileSync, copyFile } from "./copy_file";
|
||||||
export { readlinkSync, readlink } from "./read_link";
|
export { readlinkSync, readlink } from "./read_link";
|
||||||
export { FileInfo, statSync, lstatSync, stat, lstat } from "./stat";
|
export { statSync, lstatSync, stat, lstat } from "./stat";
|
||||||
export { symlinkSync, symlink } from "./symlink";
|
export { symlinkSync, symlink } from "./symlink";
|
||||||
export { writeFileSync, writeFile } from "./write_file";
|
export { writeFileSync, writeFile } from "./write_file";
|
||||||
export { ErrorKind, DenoError } from "./errors";
|
export { ErrorKind, DenoError } from "./errors";
|
||||||
|
@ -19,4 +20,5 @@ export { libdeno } from "./libdeno";
|
||||||
export { platform } from "./platform";
|
export { platform } from "./platform";
|
||||||
export { trace } from "./trace";
|
export { trace } from "./trace";
|
||||||
export { truncateSync, truncate } from "./truncate";
|
export { truncateSync, truncate } from "./truncate";
|
||||||
|
export { FileInfo } from "./fileinfo";
|
||||||
export const args: string[] = [];
|
export const args: string[] = [];
|
||||||
|
|
108
js/fileinfo.ts
Normal file
108
js/fileinfo.ts
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import * as fbs from "gen/msg_generated";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A FileInfo describes a file and is returned by `stat`, `lstat`,
|
||||||
|
* `statSync`, `lstatSync`.
|
||||||
|
*/
|
||||||
|
export interface FileInfo {
|
||||||
|
readonly _isFile: boolean;
|
||||||
|
readonly _isSymlink: boolean;
|
||||||
|
/** The size of the file, in bytes. */
|
||||||
|
len: number;
|
||||||
|
/**
|
||||||
|
* The last modification time of the file. This corresponds to the `mtime`
|
||||||
|
* field from `stat` on Unix and `ftLastWriteTime` on Windows. This may not
|
||||||
|
* be available on all platforms.
|
||||||
|
*/
|
||||||
|
modified: number | null;
|
||||||
|
/**
|
||||||
|
* The last access time of the file. This corresponds to the `atime`
|
||||||
|
* field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
|
||||||
|
* be available on all platforms.
|
||||||
|
*/
|
||||||
|
accessed: number | null;
|
||||||
|
/**
|
||||||
|
* The last access time of the file. This corresponds to the `birthtime`
|
||||||
|
* field from `stat` on Unix and `ftCreationTime` on Windows. This may not
|
||||||
|
* be available on all platforms.
|
||||||
|
*/
|
||||||
|
created: number | null;
|
||||||
|
/**
|
||||||
|
* The underlying raw st_mode bits that contain the standard Unix permissions
|
||||||
|
* for this file/directory. TODO Match behavior with Go on windows for mode.
|
||||||
|
*/
|
||||||
|
mode: number | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file or directory name.
|
||||||
|
*/
|
||||||
|
name: string | null;
|
||||||
|
|
||||||
|
/** Returns the file or directory path. */
|
||||||
|
path: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this is info for a regular file. This result is mutually
|
||||||
|
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
||||||
|
*/
|
||||||
|
isFile(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this is info for a regular directory. This result is
|
||||||
|
* mutually exclusive to `FileInfo.isFile` and `FileInfo.isSymlink`.
|
||||||
|
*/
|
||||||
|
isDirectory(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this is info for a symlink. This result is
|
||||||
|
* mutually exclusive to `FileInfo.isFile` and `FileInfo.isDirectory`.
|
||||||
|
*/
|
||||||
|
isSymlink(): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileInfoImpl implements FileInfo {
|
||||||
|
readonly _isFile: boolean;
|
||||||
|
readonly _isSymlink: boolean;
|
||||||
|
len: number;
|
||||||
|
modified: number | null;
|
||||||
|
accessed: number | null;
|
||||||
|
created: number | null;
|
||||||
|
mode: number | null;
|
||||||
|
name: string | null;
|
||||||
|
path: string | null;
|
||||||
|
|
||||||
|
/* @internal */
|
||||||
|
constructor(private _msg: fbs.StatRes) {
|
||||||
|
const modified = this._msg.modified().toFloat64();
|
||||||
|
const accessed = this._msg.accessed().toFloat64();
|
||||||
|
const created = this._msg.created().toFloat64();
|
||||||
|
const hasMode = this._msg.hasMode();
|
||||||
|
const mode = this._msg.mode(); // negative for invalid mode (Windows)
|
||||||
|
const name = this._msg.name();
|
||||||
|
const path = this._msg.path();
|
||||||
|
|
||||||
|
this._isFile = this._msg.isFile();
|
||||||
|
this._isSymlink = this._msg.isSymlink();
|
||||||
|
this.len = this._msg.len().toFloat64();
|
||||||
|
this.modified = modified ? modified : null;
|
||||||
|
this.accessed = accessed ? accessed : null;
|
||||||
|
this.created = created ? created : null;
|
||||||
|
// null on Windows
|
||||||
|
this.mode = hasMode ? mode : null;
|
||||||
|
this.name = name ? name : null;
|
||||||
|
this.path = path ? path : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFile() {
|
||||||
|
return this._isFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirectory() {
|
||||||
|
return !this._isFile && !this._isSymlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSymlink() {
|
||||||
|
return this._isSymlink;
|
||||||
|
}
|
||||||
|
}
|
49
js/read_dir.ts
Normal file
49
js/read_dir.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// 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";
|
||||||
|
import { FileInfo, FileInfoImpl } from "./fileinfo";
|
||||||
|
import { assert } from "./util";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the directory given by path and returns
|
||||||
|
* a list of file info synchronously.
|
||||||
|
*
|
||||||
|
* import { readDirSync } from "deno";
|
||||||
|
* const files = readDirSync("/");
|
||||||
|
*/
|
||||||
|
export function readDirSync(path: string): FileInfo[] {
|
||||||
|
return res(dispatch.sendSync(...req(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the directory given by path and returns a list of file info.
|
||||||
|
*
|
||||||
|
* import { readDir } from "deno";
|
||||||
|
* const files = await readDir("/");
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export async function readDir(path: string): Promise<FileInfo[]> {
|
||||||
|
return res(await dispatch.sendAsync(...req(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function req(path: string): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] {
|
||||||
|
const builder = new flatbuffers.Builder();
|
||||||
|
const path_ = builder.createString(path);
|
||||||
|
fbs.ReadDir.startReadDir(builder);
|
||||||
|
fbs.ReadDir.addPath(builder, path_);
|
||||||
|
const msg = fbs.ReadDir.endReadDir(builder);
|
||||||
|
return [builder, fbs.Any.ReadDir, msg];
|
||||||
|
}
|
||||||
|
|
||||||
|
function res(baseRes: null | fbs.Base): FileInfo[] {
|
||||||
|
assert(baseRes != null);
|
||||||
|
assert(fbs.Any.ReadDirRes === baseRes!.msgType());
|
||||||
|
const res = new fbs.ReadDirRes();
|
||||||
|
assert(baseRes!.msg(res) != null);
|
||||||
|
const fileInfos: FileInfo[] = [];
|
||||||
|
for (let i = 0; i < res.entriesLength(); i++) {
|
||||||
|
fileInfos.push(new FileInfoImpl(res.entries(i)!));
|
||||||
|
}
|
||||||
|
return fileInfos;
|
||||||
|
}
|
60
js/read_dir_test.ts
Normal file
60
js/read_dir_test.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { test, testPerm, assert, assertEqual } from "./test_util.ts";
|
||||||
|
import * as deno from "deno";
|
||||||
|
import { FileInfo } from "deno";
|
||||||
|
|
||||||
|
function assertSameContent(files: FileInfo[]) {
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.name == "subdir") {
|
||||||
|
assert(file.isDirectory());
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.name === "002_hello.ts") {
|
||||||
|
assertEqual(file.path, `tests/${file.name}`);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEqual(counter, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
testPerm({ write: true }, function readDirSyncSuccess() {
|
||||||
|
const files = deno.readDirSync("tests/");
|
||||||
|
assertSameContent(files);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function readDirSyncNotDir() {
|
||||||
|
let caughtError = false;
|
||||||
|
let src;
|
||||||
|
|
||||||
|
try {
|
||||||
|
src = deno.readDirSync("package.json");
|
||||||
|
} catch (err) {
|
||||||
|
caughtError = true;
|
||||||
|
assertEqual(err.kind, deno.ErrorKind.Other);
|
||||||
|
}
|
||||||
|
assert(caughtError);
|
||||||
|
assertEqual(src, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function readDirSyncNotFound() {
|
||||||
|
let caughtError = false;
|
||||||
|
let src;
|
||||||
|
|
||||||
|
try {
|
||||||
|
src = deno.readDirSync("bad_dir_name");
|
||||||
|
} catch (err) {
|
||||||
|
caughtError = true;
|
||||||
|
assertEqual(err.kind, deno.ErrorKind.NotFound);
|
||||||
|
}
|
||||||
|
assert(caughtError);
|
||||||
|
assertEqual(src, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
testPerm({ write: true }, async function readDirSuccess() {
|
||||||
|
const files = await deno.readDir("tests/");
|
||||||
|
assertSameContent(files);
|
||||||
|
});
|
93
js/stat.ts
93
js/stat.ts
|
@ -3,98 +3,7 @@ import * as fbs from "gen/msg_generated";
|
||||||
import { flatbuffers } from "flatbuffers";
|
import { flatbuffers } from "flatbuffers";
|
||||||
import * as dispatch from "./dispatch";
|
import * as dispatch from "./dispatch";
|
||||||
import { assert } from "./util";
|
import { assert } from "./util";
|
||||||
|
import { FileInfo, FileInfoImpl } from "./fileinfo";
|
||||||
/**
|
|
||||||
* A FileInfo describes a file and is returned by `stat`, `lstat`,
|
|
||||||
* `statSync`, `lstatSync`.
|
|
||||||
*/
|
|
||||||
export interface FileInfo {
|
|
||||||
readonly _isFile: boolean;
|
|
||||||
readonly _isSymlink: boolean;
|
|
||||||
/** The size of the file, in bytes. */
|
|
||||||
len: number;
|
|
||||||
/**
|
|
||||||
* The last modification time of the file. This corresponds to the `mtime`
|
|
||||||
* field from `stat` on Unix and `ftLastWriteTime` on Windows. This may not
|
|
||||||
* be available on all platforms.
|
|
||||||
*/
|
|
||||||
modified: number | null;
|
|
||||||
/**
|
|
||||||
* The last access time of the file. This corresponds to the `atime`
|
|
||||||
* field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
|
|
||||||
* be available on all platforms.
|
|
||||||
*/
|
|
||||||
accessed: number | null;
|
|
||||||
/**
|
|
||||||
* The last access time of the file. This corresponds to the `birthtime`
|
|
||||||
* field from `stat` on Unix and `ftCreationTime` on Windows. This may not
|
|
||||||
* be available on all platforms.
|
|
||||||
*/
|
|
||||||
created: number | null;
|
|
||||||
/**
|
|
||||||
* The underlying raw st_mode bits that contain the standard Unix permissions
|
|
||||||
* for this file/directory. TODO Match behavior with Go on windows for mode.
|
|
||||||
*/
|
|
||||||
mode: number | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this is info for a regular file. This result is mutually
|
|
||||||
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
|
||||||
*/
|
|
||||||
isFile(): boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this is info for a regular directory. This result is
|
|
||||||
* mutually exclusive to `FileInfo.isFile` and `FileInfo.isSymlink`.
|
|
||||||
*/
|
|
||||||
isDirectory(): boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this is info for a symlink. This result is
|
|
||||||
* mutually exclusive to `FileInfo.isFile` and `FileInfo.isDirectory`.
|
|
||||||
*/
|
|
||||||
isSymlink(): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileInfoImpl implements FileInfo {
|
|
||||||
readonly _isFile: boolean;
|
|
||||||
readonly _isSymlink: boolean;
|
|
||||||
len: number;
|
|
||||||
modified: number | null;
|
|
||||||
accessed: number | null;
|
|
||||||
created: number | null;
|
|
||||||
mode: number | null;
|
|
||||||
|
|
||||||
/* @internal */
|
|
||||||
constructor(private _msg: fbs.StatRes) {
|
|
||||||
const modified = this._msg.modified().toFloat64();
|
|
||||||
const accessed = this._msg.accessed().toFloat64();
|
|
||||||
const created = this._msg.created().toFloat64();
|
|
||||||
const hasMode = this._msg.hasMode();
|
|
||||||
const mode = this._msg.mode(); // negative for invalid mode (Windows)
|
|
||||||
|
|
||||||
this._isFile = this._msg.isFile();
|
|
||||||
this._isSymlink = this._msg.isSymlink();
|
|
||||||
this.len = this._msg.len().toFloat64();
|
|
||||||
this.modified = modified ? modified : null;
|
|
||||||
this.accessed = accessed ? accessed : null;
|
|
||||||
this.created = created ? created : null;
|
|
||||||
// null on Windows
|
|
||||||
this.mode = hasMode ? mode : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFile() {
|
|
||||||
return this._isFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDirectory() {
|
|
||||||
return !this._isFile && !this._isSymlink;
|
|
||||||
}
|
|
||||||
|
|
||||||
isSymlink() {
|
|
||||||
return this._isSymlink;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries the file system for information on the path provided.
|
* Queries the file system for information on the path provided.
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "./fetch_test.ts";
|
||||||
import "./os_test.ts";
|
import "./os_test.ts";
|
||||||
import "./files_test.ts";
|
import "./files_test.ts";
|
||||||
import "./read_file_test.ts";
|
import "./read_file_test.ts";
|
||||||
|
import "./read_dir_test.ts";
|
||||||
import "./write_file_test.ts";
|
import "./write_file_test.ts";
|
||||||
import "./copy_file_test.ts";
|
import "./copy_file_test.ts";
|
||||||
import "./mkdir_test.ts";
|
import "./mkdir_test.ts";
|
||||||
|
|
|
@ -76,6 +76,7 @@ pub fn msg_from_js(
|
||||||
msg::Any::Write => handle_write,
|
msg::Any::Write => handle_write,
|
||||||
msg::Any::Remove => handle_remove,
|
msg::Any::Remove => handle_remove,
|
||||||
msg::Any::ReadFile => handle_read_file,
|
msg::Any::ReadFile => handle_read_file,
|
||||||
|
msg::Any::ReadDir => handle_read_dir,
|
||||||
msg::Any::Rename => handle_rename,
|
msg::Any::Rename => handle_rename,
|
||||||
msg::Any::Readlink => handle_read_link,
|
msg::Any::Readlink => handle_read_link,
|
||||||
msg::Any::Symlink => handle_symlink,
|
msg::Any::Symlink => handle_symlink,
|
||||||
|
@ -816,6 +817,63 @@ fn handle_stat(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_read_dir(
|
||||||
|
_state: Arc<IsolateState>,
|
||||||
|
base: &msg::Base,
|
||||||
|
data: &'static mut [u8],
|
||||||
|
) -> Box<Op> {
|
||||||
|
assert_eq!(data.len(), 0);
|
||||||
|
let msg = base.msg_as_read_dir().unwrap();
|
||||||
|
let cmd_id = base.cmd_id();
|
||||||
|
let path = String::from(msg.path().unwrap());
|
||||||
|
|
||||||
|
blocking!(base.sync(), || -> OpResult {
|
||||||
|
debug!("handle_read_dir {}", path);
|
||||||
|
let builder = &mut FlatBufferBuilder::new();
|
||||||
|
let entries: Vec<_> = fs::read_dir(Path::new(&path))?
|
||||||
|
.map(|entry| {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let metadata = entry.metadata().unwrap();
|
||||||
|
let file_type = metadata.file_type();
|
||||||
|
let name = builder.create_string(entry.file_name().to_str().unwrap());
|
||||||
|
let path = builder.create_string(entry.path().to_str().unwrap());
|
||||||
|
|
||||||
|
msg::StatRes::create(
|
||||||
|
builder,
|
||||||
|
&msg::StatResArgs {
|
||||||
|
is_file: file_type.is_file(),
|
||||||
|
is_symlink: file_type.is_symlink(),
|
||||||
|
len: metadata.len(),
|
||||||
|
modified: to_seconds!(metadata.modified()),
|
||||||
|
accessed: to_seconds!(metadata.accessed()),
|
||||||
|
created: to_seconds!(metadata.created()),
|
||||||
|
name: Some(name),
|
||||||
|
path: Some(path),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let entries = builder.create_vector(&entries);
|
||||||
|
let msg = msg::ReadDirRes::create(
|
||||||
|
builder,
|
||||||
|
&msg::ReadDirResArgs {
|
||||||
|
entries: Some(entries),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(serialize_response(
|
||||||
|
cmd_id,
|
||||||
|
builder,
|
||||||
|
msg::BaseArgs {
|
||||||
|
msg: Some(msg.as_union_value()),
|
||||||
|
msg_type: msg::Any::ReadDirRes,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_write_file(
|
fn handle_write_file(
|
||||||
state: Arc<IsolateState>,
|
state: Arc<IsolateState>,
|
||||||
base: &msg::Base,
|
base: &msg::Base,
|
||||||
|
|
12
src/msg.fbs
12
src/msg.fbs
|
@ -16,6 +16,8 @@ union Any {
|
||||||
Remove,
|
Remove,
|
||||||
ReadFile,
|
ReadFile,
|
||||||
ReadFileRes,
|
ReadFileRes,
|
||||||
|
ReadDir,
|
||||||
|
ReadDirRes,
|
||||||
WriteFile,
|
WriteFile,
|
||||||
CopyFile,
|
CopyFile,
|
||||||
Rename,
|
Rename,
|
||||||
|
@ -191,6 +193,14 @@ table ReadFileRes {
|
||||||
data: [ubyte];
|
data: [ubyte];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table ReadDir {
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
table ReadDirRes {
|
||||||
|
entries: [StatRes];
|
||||||
|
}
|
||||||
|
|
||||||
table WriteFile {
|
table WriteFile {
|
||||||
filename: string;
|
filename: string;
|
||||||
data: [ubyte];
|
data: [ubyte];
|
||||||
|
@ -235,6 +245,8 @@ table StatRes {
|
||||||
created:ulong;
|
created:ulong;
|
||||||
mode: uint;
|
mode: uint;
|
||||||
has_mode: bool; // false on windows
|
has_mode: bool; // false on windows
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
table Truncate {
|
table Truncate {
|
||||||
|
|
Loading…
Add table
Reference in a new issue