Skip to content

Commit

Permalink
Implemented deno.readDir
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron Power committed Sep 13, 2018
1 parent 0ca7301 commit b36b49e
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 11 deletions.
1 change: 1 addition & 0 deletions js/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { mkdirSync, mkdir } from "./mkdir";
export { makeTempDirSync, makeTempDir } from "./make_temp_dir";
export { removeSync, remove, removeAllSync, removeAll } from "./remove";
export { readFileSync, readFile } from "./read_file";
export { readDirSync, readDir } from "./read_dir";
export { renameSync, rename } from "./rename";
export { FileInfo, statSync, lstatSync, stat, lstat } from "./stat";
export { writeFileSync, writeFile } from "./write_file";
Expand Down
55 changes: 55 additions & 0 deletions js/read_dir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 } from "./stat";
import { assert } from "./util";

/**
* Queries the file system for information on the path provided.
* `stat` Will always follow symlinks.
*
* import { stat } from "deno";
* const fileInfo = await deno.stat("hello.txt");
* assert(fileInfo.isFile());
*/
export async function readDir(filename: string): Promise<FileInfo[]> {
return res(await dispatch.sendAsync(...req(filename)));
}

/**
* Queries the file system for information on the path provided synchronously.
* `statSync` Will always follow symlinks.
*
* import { statSync } from "deno";
* const fileInfo = deno.statSync("hello.txt");
* assert(fileInfo.isFile());
*/
export function readDirSync(filename: string): FileInfo[] {
return res(dispatch.sendSync(...req(filename)));
}

function req(
filename: string,
): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] {
const builder = new flatbuffers.Builder();
const filename_ = builder.createString(filename);
fbs.ReadDir.startReadDir(builder);
fbs.ReadDir.addFilename(builder, filename_);
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 FileInfo(res.entries(i)!));
}

return fileInfos;
}
49 changes: 49 additions & 0 deletions js/read_dir_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import { test, testPerm, assert, assertEqual } from "./test_util.ts";
import * as deno from "deno";

testPerm({ write: true }, async function readDirSuccess() {
const dirName = deno.makeTempDirSync();
const enc = new TextEncoder();
const data = enc.encode("Hello");
deno.writeFileSync(`${dirName}/test.txt`, data, 0o666);
deno.writeFileSync(`${dirName}/test.rs`, data, 0o666);

const entries = deno.readDirSync(dirName);
assertEqual(entries.length, 2);

for (const entry of entries) {
assert(entry.isFile());
assert(entry.name === "test.txt" || entry.name === "test.rs");
assert(
entry.path === `${dirName}/test.txt` ||
entry.path === `${dirName}/test.rs`
);
}
});

test(async function readDirSyncNotADir() {
let caughtError = false;

try {
const src = deno.readDirSync("Cargo.toml");
} catch (err) {
caughtError = true;
assertEqual(err.kind, deno.ErrorKind.Other);
}

assert(caughtError);
});

test(async function readDirSyncNotFound() {
let caughtError = false;

try {
const src = deno.readDirSync("bad_dir_name");
} catch (err) {
caughtError = true;
assertEqual(err.kind, deno.ErrorKind.NotFound);
}

assert(caughtError);
});
26 changes: 17 additions & 9 deletions js/stat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,29 @@ export class FileInfo {
* for this file/directory. TODO Match behavior with Go on windows for mode.
*/
mode: number | null;
/**
* Returns the file or directory name. Is `null` if the path ends in `..`.
*/
name: string | null;
/** Returns the file or directory path. */
path: string;

/* @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 mode = this._msg.mode(); // negative for invalid mode (Windows)
constructor(msg: fbs.FileInfo) {
const modified = msg.modified().toFloat64();
const accessed = msg.accessed().toFloat64();
const created = msg.created().toFloat64();
const mode = msg.mode(); // negative for invalid mode (Windows)

this._isFile = this._msg.isFile();
this._isSymlink = this._msg.isSymlink();
this.len = this._msg.len().toFloat64();
this._isFile = msg.isFile();
this._isSymlink = msg.isSymlink();
this.len = msg.len().toFloat64();
this.modified = modified ? modified : null;
this.accessed = accessed ? accessed : null;
this.created = created ? created : null;
this.mode = mode >= 0 ? mode : null; // null if invalid mode (Windows)
this.name = msg.name();
this.path = msg.path()!;
}

/**
Expand Down Expand Up @@ -147,5 +155,5 @@ function res(baseRes: null | fbs.Base): FileInfo {
assert(fbs.Any.StatRes === baseRes!.msgType());
const res = new fbs.StatRes();
assert(baseRes!.msg(res) != null);
return new FileInfo(res);
return new FileInfo(res.info(0)!);
}
1 change: 1 addition & 0 deletions js/unit_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "./compiler_test.ts";
import "./console_test.ts";
import "./fetch_test.ts";
import "./os_test.ts";
import "./read_dir_test.ts";
import "./read_file_test.ts";
import "./write_file_test.ts";
import "./mkdir_test.ts";
Expand Down
80 changes: 78 additions & 2 deletions src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
msg::Any::MakeTempDir => handle_make_temp_dir,
msg::Any::Mkdir => handle_mkdir,
msg::Any::Remove => handle_remove,
msg::Any::ReadDir => handle_read_dir,
msg::Any::ReadFile => handle_read_file,
msg::Any::Rename => handle_rename,
msg::Any::SetEnv => handle_set_env,
Expand Down Expand Up @@ -543,6 +544,64 @@ fn get_mode(_perm: fs::Permissions) -> i32 {
-1
}

fn handle_read_dir(_d: *const DenoC, base: &msg::Base) -> Box<Op> {
let msg = base.msg_as_read_dir().unwrap();
let cmd_id = base.cmd_id();
let filename = String::from(msg.filename().unwrap());

Box::new(futures::future::result(|| -> OpResult {
let builder = &mut FlatBufferBuilder::new();
debug!("handle_read_dir {}", filename);
let path = Path::new(&filename);
let entries: Vec<_> = fs::read_dir(path)?
.filter_map(Result::ok)
.filter_map(|e| e.metadata().map(|m| (e, m.file_type(), m)).ok())
.filter_map(|(entry, file_type, metadata)| {
let name = entry.file_name().to_str().and_then(|s| {
Some(builder.create_string(s))
});
let path = entry.path().to_str().and_then(|s| {
Some(builder.create_string(s))
});

Some(msg::FileInfo::create(
builder,
&msg::FileInfoArgs {
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: name,
path: 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_stat(_d: *const DenoC, base: &msg::Base) -> Box<Op> {
let msg = base.msg_as_stat().unwrap();
let cmd_id = base.cmd_id();
Expand All @@ -559,17 +618,34 @@ fn handle_stat(_d: *const DenoC, base: &msg::Base) -> Box<Op> {
fs::metadata(path)?
};

let msg = msg::StatRes::create(
let name = path.file_name().and_then(std::ffi::OsStr::to_str).and_then(|s| {
Some(builder.create_string(s))
});

let path = Some(builder.create_string(&filename));

let info = msg::FileInfo::create(
builder,
&msg::StatResArgs {
&msg::FileInfoArgs {
is_file: metadata.is_file(),
is_symlink: metadata.file_type().is_symlink(),
len: metadata.len(),
modified: to_seconds!(metadata.modified()),
accessed: to_seconds!(metadata.accessed()),
created: to_seconds!(metadata.created()),
name,
path,
mode: get_mode(metadata.permissions()),
..Default::default()
}
);

let vec = builder.create_vector(&[info]);
let msg = msg::StatRes::create(
builder,
&msg::StatResArgs {
info: Some(vec),
..Default::default()
},
);

Expand Down
18 changes: 18 additions & 0 deletions src/msg.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ union Any {
MakeTempDirRes,
Mkdir,
Remove,
ReadDir,
ReadDirRes,
ReadFile,
ReadFileRes,
WriteFile,
Expand Down Expand Up @@ -200,18 +202,34 @@ table Rename {
newpath: string;
}

table ReadDir {
filename: string;
}

table ReadDirRes {
entries: [FileInfo];
}

table Stat {
filename: string;
lstat: bool;
}

table StatRes {
// Workaround for codegen issues. Without the array Flatbuffers will not
// declare a lifetime for StatRes that FileInfo requires.
info: [FileInfo];
}

table FileInfo {
is_file: bool;
is_symlink: bool;
len: ulong;
modified:ulong;
accessed:ulong;
created:ulong;
name: string;
path: string;
mode: int = -1;
// negative mode for invalid (Windows); default to invalid
}
Expand Down

0 comments on commit b36b49e

Please sign in to comment.