mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -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/errors.ts",
|
||||
"js/fetch.ts",
|
||||
"js/fileinfo.ts",
|
||||
"js/files.ts",
|
||||
"js/io.ts",
|
||||
"js/global-eval.ts",
|
||||
|
@ -88,6 +89,7 @@ ts_sources = [
|
|||
"js/platform.ts",
|
||||
"js/plugins.d.ts",
|
||||
"js/read_file.ts",
|
||||
"js/read_dir.ts",
|
||||
"js/remove.ts",
|
||||
"js/rename.ts",
|
||||
"js/read_link.ts",
|
||||
|
|
|
@ -9,9 +9,10 @@ export { makeTempDirSync, makeTempDir } from "./make_temp_dir";
|
|||
export { removeSync, remove, removeAllSync, removeAll } from "./remove";
|
||||
export { renameSync, rename } from "./rename";
|
||||
export { readFileSync, readFile } from "./read_file";
|
||||
export { readDirSync, readDir } from "./read_dir";
|
||||
export { copyFileSync, copyFile } from "./copy_file";
|
||||
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 { writeFileSync, writeFile } from "./write_file";
|
||||
export { ErrorKind, DenoError } from "./errors";
|
||||
|
@ -19,4 +20,5 @@ export { libdeno } from "./libdeno";
|
|||
export { platform } from "./platform";
|
||||
export { trace } from "./trace";
|
||||
export { truncateSync, truncate } from "./truncate";
|
||||
export { FileInfo } from "./fileinfo";
|
||||
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 * as dispatch from "./dispatch";
|
||||
import { assert } from "./util";
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
import { FileInfo, FileInfoImpl } from "./fileinfo";
|
||||
|
||||
/**
|
||||
* Queries the file system for information on the path provided.
|
||||
|
|
|
@ -7,6 +7,7 @@ import "./fetch_test.ts";
|
|||
import "./os_test.ts";
|
||||
import "./files_test.ts";
|
||||
import "./read_file_test.ts";
|
||||
import "./read_dir_test.ts";
|
||||
import "./write_file_test.ts";
|
||||
import "./copy_file_test.ts";
|
||||
import "./mkdir_test.ts";
|
||||
|
|
|
@ -76,6 +76,7 @@ pub fn msg_from_js(
|
|||
msg::Any::Write => handle_write,
|
||||
msg::Any::Remove => handle_remove,
|
||||
msg::Any::ReadFile => handle_read_file,
|
||||
msg::Any::ReadDir => handle_read_dir,
|
||||
msg::Any::Rename => handle_rename,
|
||||
msg::Any::Readlink => handle_read_link,
|
||||
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(
|
||||
state: Arc<IsolateState>,
|
||||
base: &msg::Base,
|
||||
|
|
12
src/msg.fbs
12
src/msg.fbs
|
@ -16,6 +16,8 @@ union Any {
|
|||
Remove,
|
||||
ReadFile,
|
||||
ReadFileRes,
|
||||
ReadDir,
|
||||
ReadDirRes,
|
||||
WriteFile,
|
||||
CopyFile,
|
||||
Rename,
|
||||
|
@ -191,6 +193,14 @@ table ReadFileRes {
|
|||
data: [ubyte];
|
||||
}
|
||||
|
||||
table ReadDir {
|
||||
path: string;
|
||||
}
|
||||
|
||||
table ReadDirRes {
|
||||
entries: [StatRes];
|
||||
}
|
||||
|
||||
table WriteFile {
|
||||
filename: string;
|
||||
data: [ubyte];
|
||||
|
@ -235,6 +245,8 @@ table StatRes {
|
|||
created:ulong;
|
||||
mode: uint;
|
||||
has_mode: bool; // false on windows
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
table Truncate {
|
||||
|
|
Loading…
Add table
Reference in a new issue