Skip to content

Commit

Permalink
Implemented deno.readDirSync
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron Power committed Sep 4, 2018
1 parent 20be2ed commit 81c55c4
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 31 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 {
exit,
FileInfo,
makeTempDirSync,
readDirSync,
readFileSync,
statSync,
lStatSync,
Expand Down
71 changes: 63 additions & 8 deletions js/os.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,53 @@ export function makeTempDirSync({
return path!;
}

/**
* Given a directory path, returns an array of `FileInfo` entries in the
* directory. Will error if the given path does not exist or is not directory.
* import { * as deno } from "deno";
* const entries = deno.readDirSync("dir/");
*
* function recurseDirectory(entries: deno.FileInfo) {
* for (const entry of entries) {
* console.log(`Name: ${entry.name}`)
* console.log(`Is it a file? ${entry.isFile()}`)
*
* if (entry.isDirectory()) {
* recurseDirectory(deno.readDirSync(entry.path());
* }
* }
* }
*
* recurseDirectory(entries);
*
*/
export function readDirSync(filename: string): FileInfo[] {
/* Ideally we could write
const res = send({
command: fbs.Command.OPEN_DIR_SYNC,
openDirSyncFilename: filename
});
return res.openDirSyncFiles;
*/
const builder = new flatbuffers.Builder();
const filename_ = builder.createString(filename);
fbs.OpenDirSync.startOpenDirSync(builder);
fbs.OpenDirSync.addFilename(builder, filename_);
const msg = fbs.OpenDirSync.endOpenDirSync(builder);
const baseRes = send(builder, fbs.Any.OpenDirSync, msg);
assert(baseRes != null);
assert(fbs.Any.OpenDirSyncRes === baseRes!.msgType());
const res = new fbs.OpenDirSyncRes();
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;
}

export function readFileSync(filename: string): Uint8Array {
/* Ideally we could write
const res = send({
Expand Down Expand Up @@ -212,19 +259,27 @@ export class FileInfo {
* be available on all platforms.
*/
created: 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.StatSyncRes) {
const modified = this._msg.modified().toFloat64();
const accessed = this._msg.accessed().toFloat64();
const created = this._msg.created().toFloat64();
constructor(msg: fbs.FileInfo) {
const modified = msg.modified().toFloat64();
const accessed = msg.accessed().toFloat64();
const created = msg.created().toFloat64();

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.name = msg.name();
this.path = msg.path()!;
}

/**
Expand Down Expand Up @@ -291,7 +346,7 @@ function statSyncInner(filename: string, lstat: boolean): FileInfo {
assert(fbs.Any.StatSyncRes === baseRes!.msgType());
const res = new fbs.StatSyncRes();
assert(baseRes!.msg(res) != null);
return new FileInfo(res);
return new FileInfo(res.info()!);
}

export function writeFileSync(
Expand Down
57 changes: 56 additions & 1 deletion js/os_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,21 @@ test(async function statSyncSuccess() {
assert(testingInfo.isDirectory());
assert(!testingInfo.isSymlink());

const srcInfo = deno.statSync("src");
const srcInfo = deno.statSync("src/");
assert(srcInfo.isDirectory());
assert(!srcInfo.isSymlink());

const denoInfo = deno.statSync("js/deno.ts");
assert(denoInfo.isFile());
assertEqual(denoInfo.name, "deno.ts");

const currentDirectoryInfo = deno.statSync("js/..");
assert(currentDirectoryInfo.isDirectory());
assertEqual(currentDirectoryInfo.name, null);

const jsInfo = deno.statSync("js/.");
assert(jsInfo.isDirectory());
assertEqual(jsInfo.name, "js");
});

test(async function statSyncNotFound() {
Expand Down Expand Up @@ -100,6 +112,49 @@ test(async function readFileSyncSuccess() {
assertEqual(pkg.name, "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);
console.log(entries);
assertEqual(entries.length, 2);

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

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

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

assert(caughtError);
});

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

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

assert(caughtError);
});

/* TODO We should be able to catch specific types.
test(function tests_readFileSync_NotFound() {
let caughtError = false;
Expand Down
103 changes: 87 additions & 16 deletions src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
msg::Any::TimerStart => handle_timer_start,
msg::Any::TimerClear => handle_timer_clear,
msg::Any::MakeTempDir => handle_make_temp_dir,
msg::Any::OpenDirSync => handle_open_dir_sync,
msg::Any::ReadFileSync => handle_read_file_sync,
msg::Any::SetEnv => handle_set_env,
msg::Any::StatSync => handle_stat_sync,
Expand Down Expand Up @@ -488,6 +489,63 @@ fn handle_make_temp_dir(
))
}

fn handle_open_dir_sync(
_d: *const DenoC,
base: msg::Base,
builder: &mut FlatBufferBuilder,
) -> HandlerResult {
let msg = base.msg_as_open_dir_sync().unwrap();
let filename = msg.filename().unwrap();
debug!("handle_open_dir_sync {}", filename);

let entries: Vec<_> = fs::read_dir(Path::new(filename))?
.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::OpenDirSyncRes::create(
builder,
&msg::OpenDirSyncResArgs {
entries: Some(entries),
..Default::default()
},
);

Ok(create_msg(
builder,
&msg::BaseArgs {
msg: Some(msg.as_union_value()),
msg_type: msg::Any::OpenDirSyncRes,
..Default::default()
},
))
}

// Prototype https://github.com/denoland/deno/blob/golang/os.go#L171-L184
fn handle_read_file_sync(
_d: *const DenoC,
Expand Down Expand Up @@ -518,16 +576,6 @@ fn handle_read_file_sync(
))
}

macro_rules! to_seconds {
($time:expr) => {{
// Unwrap is safe here as if the file is before the unix epoch
// something is very wrong.
$time
.and_then(|t| Ok(t.duration_since(UNIX_EPOCH).unwrap().as_secs()))
.unwrap_or(0)
}};
}

fn handle_stat_sync(
_d: *const DenoC,
base: msg::Base,
Expand All @@ -544,16 +592,30 @@ fn handle_stat_sync(
} else {
fs::metadata(path)?
};
let name = path.file_name().and_then(std::ffi::OsStr::to_str).and_then(|s| {
Some(builder.create_string(s))
});
let filename = builder.create_string(filename);

let msg = msg::StatSyncRes::create(
let info = msg::FileInfo::create(
builder,
&msg::StatSyncResArgs {
is_file: metadata.is_file(),
&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()),
modified: to_seconds(metadata.modified()),
accessed: to_seconds(metadata.accessed()),
created: to_seconds(metadata.created()),
name: name,
path: Some(filename),
..Default::default()
},
);

let msg = msg::StatSyncRes::create(
builder,
&msg::StatSyncResArgs {
info: Some(info),
..Default::default()
},
);
Expand Down Expand Up @@ -648,3 +710,12 @@ fn handle_timer_clear(
remove_timer(d, msg.id());
Ok(null_buf())
}

// Convert file metadata times to seconds if they're available, if not
// return zero.
fn to_seconds(time: std::io::Result<std::time::SystemTime>) -> u64 {
// Unwrap is safe here as if the file is before the unix epoch
// something is very wrong.
time.and_then(|t| Ok(t.duration_since(UNIX_EPOCH).unwrap().as_secs()))
.unwrap_or(0)
}
29 changes: 23 additions & 6 deletions src/msg.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ union Any {
FetchRes,
MakeTempDir,
MakeTempDirRes,
OpenDirSync,
OpenDirSyncRes,
ReadFileSync,
ReadFileSyncRes,
StatSync,
Expand Down Expand Up @@ -156,6 +158,18 @@ table FetchRes {
body: [ubyte];
}

table FileInfo {
is_file: bool;
is_symlink: bool;
len: ulong;
modified:ulong;
accessed:ulong;
created:ulong;
// TODO replace both `name` and `path` with solution to #627.
name: string;
path: string;
}

table MakeTempDir {
dir: string;
prefix: string;
Expand All @@ -166,6 +180,14 @@ table MakeTempDirRes {
path: string;
}

table OpenDirSync {
filename: string;
}

table OpenDirSyncRes {
entries: [FileInfo];
}

table ReadFileSync {
filename: string;
}
Expand All @@ -180,12 +202,7 @@ table StatSync {
}

table StatSyncRes {
is_file: bool;
is_symlink: bool;
len: ulong;
modified:ulong;
accessed:ulong;
created:ulong;
info: FileInfo;
}

table WriteFileSync {
Expand Down

0 comments on commit 81c55c4

Please sign in to comment.