mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
feat(fs): add more unix-only fields to FileInfo (#3680)
This commit is contained in:
parent
5856d21a2e
commit
91757f63fd
5 changed files with 204 additions and 44 deletions
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { StatResponse } from "./stat.ts";
|
||||
import { build } from "./build.ts";
|
||||
|
||||
/** A FileInfo describes a file and is returned by `stat`, `lstat`,
|
||||
* `statSync`, `lstatSync`.
|
||||
|
@ -22,13 +23,38 @@ export interface FileInfo {
|
|||
* be available on all platforms.
|
||||
*/
|
||||
created: number | null;
|
||||
|
||||
/** The file or directory name. */
|
||||
name: string | null;
|
||||
|
||||
/** ID of the device containing the file. Unix only. */
|
||||
dev: number | null;
|
||||
|
||||
/** Inode number. Unix only. */
|
||||
ino: 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;
|
||||
|
||||
/** The file or directory name. */
|
||||
name: string | null;
|
||||
/** Number of hard links pointing to this file. Unix only. */
|
||||
nlink: number | null;
|
||||
|
||||
/** User ID of the owner of this file. Unix only. */
|
||||
uid: number | null;
|
||||
|
||||
/** User ID of the owner of this file. Unix only. */
|
||||
gid: number | null;
|
||||
|
||||
/** Device ID of this file. Unix only. */
|
||||
rdev: number | null;
|
||||
|
||||
/** Blocksize for filesystem I/O. Unix only. */
|
||||
blksize: number | null;
|
||||
|
||||
/** Number of blocks allocated to the file, in 512-byte units. Unix only. */
|
||||
blocks: number | null;
|
||||
|
||||
/** Returns whether this is info for a regular file. This result is mutually
|
||||
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
||||
|
@ -54,17 +80,37 @@ export class FileInfoImpl implements FileInfo {
|
|||
modified: number | null;
|
||||
accessed: number | null;
|
||||
created: number | null;
|
||||
mode: number | null;
|
||||
name: string | null;
|
||||
|
||||
dev: number | null;
|
||||
ino: number | null;
|
||||
mode: number | null;
|
||||
nlink: number | null;
|
||||
uid: number | null;
|
||||
gid: number | null;
|
||||
rdev: number | null;
|
||||
blksize: number | null;
|
||||
blocks: number | null;
|
||||
|
||||
/* @internal */
|
||||
constructor(private _res: StatResponse) {
|
||||
const isUnix = build.os === "mac" || build.os === "linux";
|
||||
const modified = this._res.modified;
|
||||
const accessed = this._res.accessed;
|
||||
const created = this._res.created;
|
||||
const hasMode = this._res.hasMode;
|
||||
const mode = this._res.mode; // negative for invalid mode (Windows)
|
||||
const name = this._res.name;
|
||||
// Unix only
|
||||
const {
|
||||
dev,
|
||||
ino,
|
||||
mode,
|
||||
nlink,
|
||||
uid,
|
||||
gid,
|
||||
rdev,
|
||||
blksize,
|
||||
blocks
|
||||
} = this._res;
|
||||
|
||||
this._isFile = this._res.isFile;
|
||||
this._isSymlink = this._res.isSymlink;
|
||||
|
@ -72,9 +118,17 @@ export class FileInfoImpl implements FileInfo {
|
|||
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;
|
||||
// Only non-null if on Unix
|
||||
this.dev = isUnix ? dev : null;
|
||||
this.ino = isUnix ? ino : null;
|
||||
this.mode = isUnix ? mode : null;
|
||||
this.nlink = isUnix ? nlink : null;
|
||||
this.uid = isUnix ? uid : null;
|
||||
this.gid = isUnix ? gid : null;
|
||||
this.rdev = isUnix ? rdev : null;
|
||||
this.blksize = isUnix ? blksize : null;
|
||||
this.blocks = isUnix ? blocks : null;
|
||||
}
|
||||
|
||||
isFile(): boolean {
|
||||
|
|
31
cli/js/lib.deno_runtime.d.ts
vendored
31
cli/js/lib.deno_runtime.d.ts
vendored
|
@ -732,12 +732,28 @@ declare namespace Deno {
|
|||
* be available on all platforms.
|
||||
*/
|
||||
created: number | null;
|
||||
/** The file or directory name. */
|
||||
name: string | null;
|
||||
/** ID of the device containing the file. Unix only. */
|
||||
dev: number | null;
|
||||
/** Inode number. Unix only. */
|
||||
ino: 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;
|
||||
/** The file or directory name. */
|
||||
name: string | null;
|
||||
/** Number of hard links pointing to this file. Unix only. */
|
||||
nlink: number | null;
|
||||
/** User ID of the owner of this file. Unix only. */
|
||||
uid: number | null;
|
||||
/** User ID of the owner of this file. Unix only. */
|
||||
gid: number | null;
|
||||
/** Device ID of this file. Unix only. */
|
||||
rdev: number | null;
|
||||
/** Blocksize for filesystem I/O. Unix only. */
|
||||
blksize: number | null;
|
||||
/** Number of blocks allocated to the file, in 512-byte units. Unix only. */
|
||||
blocks: number | null;
|
||||
/** Returns whether this is info for a regular file. This result is mutually
|
||||
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
||||
*/
|
||||
|
@ -827,9 +843,16 @@ declare namespace Deno {
|
|||
modified: number;
|
||||
accessed: number;
|
||||
created: number;
|
||||
mode: number;
|
||||
hasMode: boolean;
|
||||
name: string | null;
|
||||
dev: number;
|
||||
ino: number;
|
||||
mode: number;
|
||||
nlink: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
rdev: number;
|
||||
blksize: number;
|
||||
blocks: number;
|
||||
}
|
||||
/** Queries the file system for information on the path provided. If the given
|
||||
* path is a symlink information about the symlink will be returned.
|
||||
|
|
|
@ -10,9 +10,17 @@ export interface StatResponse {
|
|||
modified: number;
|
||||
accessed: number;
|
||||
created: number;
|
||||
mode: number;
|
||||
hasMode: boolean; // false on windows
|
||||
name: string | null;
|
||||
// Unix only members
|
||||
dev: number;
|
||||
ino: number;
|
||||
mode: number;
|
||||
nlink: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
rdev: number;
|
||||
blksize: number;
|
||||
blocks: number;
|
||||
}
|
||||
|
||||
/** Queries the file system for information on the path provided. If the given
|
||||
|
|
|
@ -170,3 +170,53 @@ testPerm({ read: true }, async function lstatNotFound(): Promise<void> {
|
|||
assert(caughtError);
|
||||
assertEquals(badInfo, undefined);
|
||||
});
|
||||
|
||||
const isWindows = Deno.build.os === "win";
|
||||
|
||||
// OS dependent tests
|
||||
if (isWindows) {
|
||||
testPerm(
|
||||
{ read: true, write: true },
|
||||
async function statNoUnixFields(): Promise<void> {
|
||||
const enc = new TextEncoder();
|
||||
const data = enc.encode("Hello");
|
||||
const tempDir = Deno.makeTempDirSync();
|
||||
const filename = tempDir + "/test.txt";
|
||||
Deno.writeFileSync(filename, data, { perm: 0o666 });
|
||||
const s = Deno.statSync(filename);
|
||||
assert(s.dev === null);
|
||||
assert(s.ino === null);
|
||||
assert(s.mode === null);
|
||||
assert(s.nlink === null);
|
||||
assert(s.uid === null);
|
||||
assert(s.gid === null);
|
||||
assert(s.rdev === null);
|
||||
assert(s.blksize === null);
|
||||
assert(s.blocks === null);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
testPerm(
|
||||
{ read: true, write: true },
|
||||
async function statUnixFields(): Promise<void> {
|
||||
const enc = new TextEncoder();
|
||||
const data = enc.encode("Hello");
|
||||
const tempDir = Deno.makeTempDirSync();
|
||||
const filename = tempDir + "/test.txt";
|
||||
const filename2 = tempDir + "/test2.txt";
|
||||
Deno.writeFileSync(filename, data, { perm: 0o666 });
|
||||
// Create a link
|
||||
Deno.linkSync(filename, filename2);
|
||||
const s = Deno.statSync(filename);
|
||||
assert(s.dev !== null);
|
||||
assert(s.ino !== null);
|
||||
assertEquals(s.mode & 0o666, 0o666);
|
||||
assertEquals(s.nlink, 2);
|
||||
assert(s.uid !== null);
|
||||
assert(s.gid !== null);
|
||||
assert(s.rdev !== null);
|
||||
assert(s.blksize !== null);
|
||||
assert(s.blocks !== null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ use std::fs;
|
|||
use std::path::PathBuf;
|
||||
use std::time::UNIX_EPOCH;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
|
@ -226,14 +228,55 @@ macro_rules! to_seconds {
|
|||
}};
|
||||
}
|
||||
|
||||
#[cfg(any(unix))]
|
||||
fn get_mode(perm: &fs::Permissions) -> u32 {
|
||||
perm.mode()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn get_stat_json(
|
||||
metadata: fs::Metadata,
|
||||
maybe_name: Option<String>,
|
||||
) -> Result<Value, ErrBox> {
|
||||
// Unix stat member (number types only). 0 if not on unix.
|
||||
macro_rules! usm {
|
||||
($member: ident) => {{
|
||||
#[cfg(unix)]
|
||||
{
|
||||
metadata.$member()
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
0
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix)))]
|
||||
fn get_mode(_perm: &fs::Permissions) -> u32 {
|
||||
0
|
||||
let mut json_val = json!({
|
||||
"isFile": metadata.is_file(),
|
||||
"isSymlink": metadata.file_type().is_symlink(),
|
||||
"len": metadata.len(),
|
||||
// In seconds. Available on both Unix or Windows.
|
||||
"modified":to_seconds!(metadata.modified()),
|
||||
"accessed":to_seconds!(metadata.accessed()),
|
||||
"created":to_seconds!(metadata.created()),
|
||||
// Following are only valid under Unix.
|
||||
"dev": usm!(dev),
|
||||
"ino": usm!(ino),
|
||||
"mode": usm!(mode),
|
||||
"nlink": usm!(nlink),
|
||||
"uid": usm!(uid),
|
||||
"gid": usm!(gid),
|
||||
"rdev": usm!(rdev),
|
||||
// TODO(kevinkassimo): *time_nsec requires BigInt.
|
||||
// Probably should be treated as String if we need to add them.
|
||||
"blksize": usm!(blksize),
|
||||
"blocks": usm!(blocks),
|
||||
});
|
||||
|
||||
// "name" is an optional field by our design.
|
||||
if let Some(name) = maybe_name {
|
||||
if let serde_json::Value::Object(ref mut m) = json_val {
|
||||
m.insert("name".to_owned(), json!(name));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(json_val)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -265,17 +308,7 @@ fn op_stat(
|
|||
} else {
|
||||
fs::metadata(&filename)?
|
||||
};
|
||||
|
||||
Ok(json!({
|
||||
"isFile": metadata.is_file(),
|
||||
"isSymlink": metadata.file_type().is_symlink(),
|
||||
"len": metadata.len(),
|
||||
"modified":to_seconds!(metadata.modified()),
|
||||
"accessed":to_seconds!(metadata.accessed()),
|
||||
"created":to_seconds!(metadata.created()),
|
||||
"mode": get_mode(&metadata.permissions()),
|
||||
"hasMode": cfg!(target_family = "unix"), // false on windows,
|
||||
}))
|
||||
get_stat_json(metadata, None)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -335,19 +368,11 @@ fn op_read_dir(
|
|||
.map(|entry| {
|
||||
let entry = entry.unwrap();
|
||||
let metadata = entry.metadata().unwrap();
|
||||
let file_type = metadata.file_type();
|
||||
|
||||
json!({
|
||||
"isFile": file_type.is_file(),
|
||||
"isSymlink": file_type.is_symlink(),
|
||||
"len": metadata.len(),
|
||||
"modified": to_seconds!(metadata.modified()),
|
||||
"accessed": to_seconds!(metadata.accessed()),
|
||||
"created": to_seconds!(metadata.created()),
|
||||
"mode": get_mode(&metadata.permissions()),
|
||||
"name": entry.file_name().to_str().unwrap(),
|
||||
"hasMode": cfg!(target_family = "unix"), // false on windows,
|
||||
})
|
||||
get_stat_json(
|
||||
metadata,
|
||||
Some(entry.file_name().to_str().unwrap().to_owned()),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
Loading…
Reference in a new issue