1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 15:24:46 -05:00

Implement deno.symlink() (#742)

This commit is contained in:
Mani Maghsoudlou 2018-09-18 21:38:24 -07:00 committed by Ryan Dahl
parent d19268b2bf
commit 017ef096df
7 changed files with 166 additions and 1 deletions

View file

@ -76,6 +76,7 @@ ts_sources = [
"js/remove.ts", "js/remove.ts",
"js/rename.ts", "js/rename.ts",
"js/stat.ts", "js/stat.ts",
"js/symlink.ts",
"js/text_encoding.ts", "js/text_encoding.ts",
"js/timers.ts", "js/timers.ts",
"js/types.ts", "js/types.ts",

View file

@ -8,6 +8,7 @@ export { removeSync, remove, removeAllSync, removeAll } from "./remove";
export { readFileSync, readFile } from "./read_file"; export { readFileSync, readFile } from "./read_file";
export { renameSync, rename } from "./rename"; export { renameSync, rename } from "./rename";
export { FileInfo, statSync, lstatSync, stat, lstat } from "./stat"; export { FileInfo, statSync, lstatSync, stat, lstat } from "./stat";
export { symlinkSync, symlink } from "./symlink";
export { writeFileSync, writeFile } from "./write_file"; export { writeFileSync, writeFile } from "./write_file";
export { ErrorKind, DenoError } from "./errors"; export { ErrorKind, DenoError } from "./errors";
export { libdeno } from "./libdeno"; export { libdeno } from "./libdeno";

56
js/symlink.ts Normal file
View file

@ -0,0 +1,56 @@
// 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 * as util from "./util";
/**
* Synchronously creates newname as a symbolic link to oldname.
* The type argument can be set to 'dir' or 'file' and is only
* available on Windows (ignored on other platforms).
*
* import { symlinkSync } from "deno";
* symlinkSync("old/name", "new/name");
*/
export function symlinkSync(
oldname: string,
newname: string,
type?: string
): void {
dispatch.sendSync(...req(oldname, newname, type));
}
/**
* Creates newname as a symbolic link to oldname.
* The type argument can be set to 'dir' or 'file' and is only
* available on Windows (ignored on other platforms).
*
* import { symlink } from "deno";
* await symlink("old/name", "new/name");
*/
export async function symlink(
oldname: string,
newname: string,
type?: string
): Promise<void> {
await dispatch.sendAsync(...req(oldname, newname, type));
}
function req(
oldname: string,
newname: string,
type?: string
): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] {
// TODO Use type for Windows.
if (type) {
return util.notImplemented();
}
const builder = new flatbuffers.Builder();
const oldname_ = builder.createString(oldname);
const newname_ = builder.createString(newname);
fbs.Symlink.startSymlink(builder);
fbs.Symlink.addOldname(builder, oldname_);
fbs.Symlink.addNewname(builder, newname_);
const msg = fbs.Symlink.endSymlink(builder);
return [builder, fbs.Any.Symlink, msg];
}

71
js/symlink_test.ts Normal file
View file

@ -0,0 +1,71 @@
// 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 }, function symlinkSyncSuccess() {
const testDir = deno.makeTempDirSync() + "/test-symlink-sync";
const oldname = testDir + "/oldname";
const newname = testDir + "/newname";
deno.mkdirSync(oldname);
let errOnWindows;
// Just for now, until we implement symlink for Windows.
try {
deno.symlinkSync(oldname, newname);
} catch (e) {
errOnWindows = e;
}
if (errOnWindows) {
assertEqual(errOnWindows.kind, deno.ErrorKind.Other);
assertEqual(errOnWindows.message, "Not implemented");
} else {
const newNameInfoLStat = deno.lstatSync(newname);
const newNameInfoStat = deno.statSync(newname);
assert(newNameInfoLStat.isSymlink());
assert(newNameInfoStat.isDirectory());
}
});
testPerm({ write: false }, function symlinkSyncPerm() {
let err;
try {
deno.symlinkSync("oldbaddir", "newbaddir");
} catch (e) {
err = e;
}
assertEqual(err.kind, deno.ErrorKind.PermissionDenied);
assertEqual(err.name, "PermissionDenied");
});
// Just for now, until we implement symlink for Windows.
testPerm({ write: true }, function symlinkSyncNotImplemented() {
let err;
try {
deno.symlinkSync("oldname", "newname", "dir");
} catch (e) {
err = e;
}
assertEqual(err.message, "Not implemented");
});
testPerm({ write: true }, async function symlinkSuccess() {
const testDir = deno.makeTempDirSync() + "/test-symlink";
const oldname = testDir + "/oldname";
const newname = testDir + "/newname";
deno.mkdirSync(oldname);
let errOnWindows;
// Just for now, until we implement symlink for Windows.
try {
await deno.symlink(oldname, newname);
} catch (e) {
errOnWindows = e;
}
if (errOnWindows) {
assertEqual(errOnWindows.kind, deno.ErrorKind.Other);
assertEqual(errOnWindows.message, "Not implemented");
} else {
const newNameInfoLStat = deno.lstatSync(newname);
const newNameInfoStat = deno.statSync(newname);
assert(newNameInfoLStat.isSymlink());
assert(newNameInfoStat.isDirectory());
}
});

View file

@ -13,3 +13,4 @@ import "./stat_test.ts";
import "./rename_test.ts"; import "./rename_test.ts";
import "./blob_test.ts"; import "./blob_test.ts";
import "./timers_test.ts"; import "./timers_test.ts";
import "./symlink_test.ts";

View file

@ -56,6 +56,7 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
msg::Any::Remove => handle_remove, msg::Any::Remove => handle_remove,
msg::Any::ReadFile => handle_read_file, msg::Any::ReadFile => handle_read_file,
msg::Any::Rename => handle_rename, msg::Any::Rename => handle_rename,
msg::Any::Symlink => handle_symlink,
msg::Any::SetEnv => handle_set_env, msg::Any::SetEnv => handle_set_env,
msg::Any::Stat => handle_stat, msg::Any::Stat => handle_stat,
msg::Any::WriteFile => handle_write_file, msg::Any::WriteFile => handle_write_file,
@ -142,6 +143,13 @@ fn permission_denied() -> DenoError {
)) ))
} }
fn not_implemented() -> DenoError {
DenoError::from(std::io::Error::new(
std::io::ErrorKind::Other,
"Not implemented"
))
}
fn handle_exit(_d: *const DenoC, base: &msg::Base) -> Box<Op> { fn handle_exit(_d: *const DenoC, base: &msg::Base) -> Box<Op> {
let msg = base.msg_as_exit().unwrap(); let msg = base.msg_as_exit().unwrap();
std::process::exit(msg.code()) std::process::exit(msg.code())
@ -663,7 +671,7 @@ fn handle_rename(d: *const DenoC, base: &msg::Base) -> Box<Op> {
let isolate = from_c(d); let isolate = from_c(d);
if !isolate.flags.allow_write { if !isolate.flags.allow_write {
return odd_future(permission_denied()); return odd_future(permission_denied());
}; }
let msg = base.msg_as_rename().unwrap(); let msg = base.msg_as_rename().unwrap();
let oldpath = String::from(msg.oldpath().unwrap()); let oldpath = String::from(msg.oldpath().unwrap());
let newpath = String::from(msg.newpath().unwrap()); let newpath = String::from(msg.newpath().unwrap());
@ -673,3 +681,24 @@ fn handle_rename(d: *const DenoC, base: &msg::Base) -> Box<Op> {
Ok(None) Ok(None)
}())) }()))
} }
fn handle_symlink(d: *const DenoC, base: &msg::Base) -> Box<Op> {
let deno = from_c(d);
if !deno.flags.allow_write {
return odd_future(permission_denied());
}
// TODO Use type for Windows.
if cfg!(windows) {
return odd_future(not_implemented());
} else {
let msg = base.msg_as_symlink().unwrap();
let oldname = String::from(msg.oldname().unwrap());
let newname = String::from(msg.newname().unwrap());
Box::new(futures::future::result(|| -> OpResult {
debug!("handle_symlink {} {}", oldname, newname);
#[cfg(any(unix))]
std::os::unix::fs::symlink(Path::new(&oldname), Path::new(&newname))?;
Ok(None)
}()))
}
}

View file

@ -20,6 +20,7 @@ union Any {
ReadFileRes, ReadFileRes,
WriteFile, WriteFile,
Rename, Rename,
Symlink,
Stat, Stat,
StatRes, StatRes,
SetEnv, SetEnv,
@ -200,6 +201,11 @@ table Rename {
newpath: string; newpath: string;
} }
table Symlink {
oldname: string;
newname: string;
}
table Stat { table Stat {
filename: string; filename: string;
lstat: bool; lstat: bool;