diff options
author | Aaron Power <theaaronepower@gmail.com> | 2018-08-29 14:22:25 +0100 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2018-08-30 12:39:33 -0400 |
commit | b86bf4d8a4d19f8ba50615028d711413307487e9 (patch) | |
tree | 8c0f40147723ba210f9c4507772e593e11e79fb5 | |
parent | d8d5c421c33b1cc5416ff87f6a7c3837e5176d4d (diff) |
Implemented statSync and lStatSync
-rw-r--r-- | js/deno.ts | 10 | ||||
-rw-r--r-- | js/os.ts | 75 | ||||
-rw-r--r-- | js/unit_tests.ts | 64 | ||||
-rw-r--r-- | src/handlers.rs | 55 | ||||
-rw-r--r-- | src/msg.fbs | 16 |
5 files changed, 219 insertions, 1 deletions
diff --git a/js/deno.ts b/js/deno.ts index 7a5c4217e..e24345ccd 100644 --- a/js/deno.ts +++ b/js/deno.ts @@ -1,5 +1,13 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. // Public deno module. -export { exit, makeTempDirSync, readFileSync, writeFileSync } from "./os"; +export { + exit, + FileInfo, + makeTempDirSync, + readFileSync, + statSync, + lStatSync, + writeFileSync +} from "./os"; export { libdeno } from "./libdeno"; export const argv: string[] = []; @@ -169,6 +169,81 @@ export function readFileSync(filename: string): Uint8Array { return new Uint8Array(dataArray!); } +export class FileInfo { + private _isFile: boolean; + private _isSymlink: boolean; + len: number; + modified: number; + accessed: number; + // Creation time is not available on all platforms. + created: number | null; + + /* @internal */ + constructor(private _msg: fbs.StatSyncRes) { + const created = this._msg.created().toFloat64(); + + this._isFile = this._msg.isFile(); + this._isSymlink = this._msg.isSymlink(); + this.len = this._msg.len().toFloat64(); + this.modified = this._msg.modified().toFloat64(); + this.accessed = this._msg.accessed().toFloat64(); + this.created = created ? created: null; + } + + isFile() { + return this._isFile; + } + + isDirectory() { + return !this._isFile && !this._isSymlink; + } + + isSymlink() { + return this._isSymlink; + } +} + +export function lStatSync(filename: string): FileInfo { + return statSyncInner(filename, true); +} + +export function statSync(filename: string): FileInfo { + return statSyncInner(filename, false); +} + +function statSyncInner(filename: string, lstat: boolean): FileInfo { + /* Ideally we could write + const res = send({ + command: fbs.Command.STAT_FILE_SYNC, + StatFilename: filename, + StatLStat: lstat, + }); + return new FileInfo(res); + */ + const builder = new flatbuffers.Builder(); + const filename_ = builder.createString(filename); + fbs.StatSync.startStatSync(builder); + fbs.StatSync.addFilename(builder, filename_); + fbs.StatSync.addLstat(builder, lstat); + const msg = fbs.StatSync.endStatSync(builder); + fbs.Base.startBase(builder); + fbs.Base.addMsg(builder, msg); + fbs.Base.addMsgType(builder, fbs.Any.StatSync); + builder.finish(fbs.Base.endBase(builder)); + const resBuf = libdeno.send(builder.asUint8Array()); + assert(resBuf != null); + // TypeScript does not track `assert` from a CFA perspective, therefore not + // null assertion `!` + const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf!)); + const baseRes = fbs.Base.getRootAsBase(bb); + maybeThrowError(baseRes); + assert(fbs.Any.StatSyncRes === baseRes.msgType()); + const res = new fbs.StatSyncRes(); + assert(baseRes.msg(res) != null); + // TypeScript cannot track assertion above, therefore not null assertion + return new FileInfo(baseRes.msg(res)!); +} + export function writeFileSync( filename: string, data: Uint8Array, diff --git a/js/unit_tests.ts b/js/unit_tests.ts index 92957c493..e1b04d4c4 100644 --- a/js/unit_tests.ts +++ b/js/unit_tests.ts @@ -13,6 +13,70 @@ test(async function tests_test() { assert(true); }); +// TODO Add tests for modified, accessed, and created fields once there is a way +// to create temp files. +test(async function statSyncSuccess() { + const packageInfo = deno.statSync("package.json"); + assert(packageInfo.isFile()); + assert(!packageInfo.isSymlink()); + + const testingInfo = deno.statSync("testing"); + assert(testingInfo.isDirectory()); + assert(!testingInfo.isSymlink()); + + const srcInfo = deno.statSync("src"); + assert(srcInfo.isDirectory()); + assert(!srcInfo.isSymlink()); +}) + +test(async function statSyncNotFound() { + let caughtError = false; + let badInfo; + + try { + badInfo = deno.statSync("bad_file_name"); + } catch (err) { + caughtError = true; + // TODO assert(err instanceof deno.NotFound). + assert(err); + assertEqual(err.name, "deno.NotFound"); + } + + assert(caughtError); + assertEqual(badInfo, undefined); +}); + +test(async function lStatSyncSuccess() { + const packageInfo = deno.lStatSync("package.json"); + assert(packageInfo.isFile()); + assert(!packageInfo.isSymlink()); + + const testingInfo = deno.lStatSync("testing"); + assert(!testingInfo.isDirectory()); + assert(testingInfo.isSymlink()); + + const srcInfo = deno.lStatSync("src"); + assert(srcInfo.isDirectory()); + assert(!srcInfo.isSymlink()); +}) + +test(async function lStatSyncNotFound() { + let caughtError = false; + let badInfo; + + try { + badInfo = deno.lStatSync("bad_file_name"); + } catch (err) { + caughtError = true; + // TODO assert(err instanceof deno.NotFound). + assert(err); + assertEqual(err.name, "deno.NotFound"); + } + + assert(caughtError); + assertEqual(badInfo, undefined); +}); + test(async function tests_readFileSync() { const data = deno.readFileSync("package.json"); if (!data.byteLength) { diff --git a/src/handlers.rs b/src/handlers.rs index 7ee6018cc..564f5e5be 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -14,6 +14,7 @@ use hyper::Client; use msg_generated::deno as msg; use std; use std::fs; +use std::time::UNIX_EPOCH; use std::path::Path; use std::time::{Duration, Instant}; use tokio::prelude::future; @@ -78,6 +79,13 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) { let filename = msg.filename().unwrap(); handle_read_file_sync(d, &mut builder, filename) } + msg::Any::StatSync => { + // TODO base.msg_as_StatSync(); + let msg = msg::StatSync::init_from_table(base.msg().unwrap()); + let filename = msg.filename().unwrap(); + let lstat = msg.lstat(); + handle_stat_sync(d, &mut builder, filename, lstat) + } msg::Any::WriteFileSync => { // TODO base.msg_as_WriteFileSync(); let msg = msg::WriteFileSync::init_from_table(base.msg().unwrap()); @@ -467,6 +475,53 @@ 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, + builder: &mut FlatBufferBuilder, + filename: &str, + lstat: bool, +) -> HandlerResult { + debug!("handle_stat_sync {} {}", filename, lstat); + let path = Path::new(filename); + let metadata = if lstat { + fs::symlink_metadata(path)? + } else { + fs::metadata(path)? + }; + + let msg = msg::StatSyncRes::create( + builder, + &msg::StatSyncResArgs { + 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()), + ..Default::default() + }, + ); + + Ok(create_msg( + builder, + &msg::BaseArgs { + msg: Some(flatbuffers::Offset::new(msg.value())), + msg_type: msg::Any::StatSyncRes, + ..Default::default() + }, + )) +} + fn handle_write_file_sync( d: *const DenoC, _builder: &mut FlatBufferBuilder, diff --git a/src/msg.fbs b/src/msg.fbs index 162a8ddee..eb3753d75 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -16,6 +16,8 @@ union Any { MakeTempDirRes, ReadFileSync, ReadFileSyncRes, + StatSync, + StatSyncRes, WriteFileSync, } @@ -153,6 +155,20 @@ table ReadFileSyncRes { data: [ubyte]; } +table StatSync { + filename: string; + lstat: bool; +} + +table StatSyncRes { + is_file: bool; + is_symlink: bool; + len: ulong; + modified:ulong; + accessed:ulong; + created:ulong; +} + table WriteFileSync { filename: string; data: [ubyte]; |