summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAxetroy <axetroy.dev@gmail.com>2019-04-22 23:35:14 +0800
committerRyan Dahl <ry@tinyclouds.org>2019-04-22 11:35:14 -0400
commitbbdd51574c31c5c415176208187bc884ccaae3c6 (patch)
treee2b388f43aef7ffb1d1f5593a839568cad06d396
parenteff23abc3280026cb44e3cf41e91bc804afbf969 (diff)
fs: add ensureLink/ensureLinkSync (denoland/deno_std#353)
Original: https://github.com/denoland/deno_std/commit/45eebed3bfa486b80da673673674f2e86abdffe7
-rw-r--r--fs/ensure_link.ts53
-rw-r--r--fs/ensure_link_test.ts170
-rw-r--r--fs/mod.ts1
3 files changed, 224 insertions, 0 deletions
diff --git a/fs/ensure_link.ts b/fs/ensure_link.ts
new file mode 100644
index 000000000..a1f512c7c
--- /dev/null
+++ b/fs/ensure_link.ts
@@ -0,0 +1,53 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+import * as path from "./path/mod.ts";
+import { ensureDir, ensureDirSync } from "./ensure_dir.ts";
+import { exists, existsSync } from "./exists.ts";
+import { PathType, getFileInfoType } from "./utils.ts";
+
+/**
+ * Ensures that the hard link exists.
+ * If the directory structure does not exist, it is created.
+ *
+ * @param src the source file path. Directory hard links are not allowed.
+ * @param dest the destination link path
+ */
+export async function ensureLink(src: string, dest: string): Promise<void> {
+ if (await exists(dest)) {
+ const destStatInfo = await Deno.lstat(dest);
+ const destFilePathType = getFileInfoType(destStatInfo);
+ if (destFilePathType !== PathType.file) {
+ throw new Error(
+ `Ensure path exists, expected 'file', got '${destFilePathType}'`
+ );
+ }
+ return;
+ }
+
+ await ensureDir(path.dirname(dest));
+
+ await Deno.link(src, dest);
+}
+
+/**
+ * Ensures that the hard link exists.
+ * If the directory structure does not exist, it is created.
+ *
+ * @param src the source file path. Directory hard links are not allowed.
+ * @param dest the destination link path
+ */
+export function ensureLinkSync(src: string, dest: string): void {
+ if (existsSync(dest)) {
+ const destStatInfo = Deno.lstatSync(dest);
+ const destFilePathType = getFileInfoType(destStatInfo);
+ if (destFilePathType !== PathType.file) {
+ throw new Error(
+ `Ensure path exists, expected 'file', got '${destFilePathType}'`
+ );
+ }
+ return;
+ }
+
+ ensureDirSync(path.dirname(dest));
+
+ Deno.linkSync(src, dest);
+}
diff --git a/fs/ensure_link_test.ts b/fs/ensure_link_test.ts
new file mode 100644
index 000000000..02beaa87d
--- /dev/null
+++ b/fs/ensure_link_test.ts
@@ -0,0 +1,170 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+// TODO(axetroy): Add test for Windows once symlink is implemented for Windows.
+import { test } from "../testing/mod.ts";
+import {
+ assertEquals,
+ assertThrows,
+ assertThrowsAsync
+} from "../testing/asserts.ts";
+import { ensureLink, ensureLinkSync } from "./ensure_link.ts";
+import * as path from "./path/mod.ts";
+
+const testdataDir = path.resolve("fs", "testdata");
+
+test(async function ensureLinkIfItNotExist() {
+ const srcDir = path.join(testdataDir, "ensure_link_1");
+ const destDir = path.join(testdataDir, "ensure_link_1_2");
+ const testFile = path.join(srcDir, "test.txt");
+ const linkFile = path.join(destDir, "link.txt");
+
+ await assertThrowsAsync(async () => {
+ await ensureLink(testFile, linkFile);
+ });
+
+ await Deno.remove(destDir, { recursive: true });
+});
+
+test(function ensureLinkSyncIfItNotExist() {
+ const testDir = path.join(testdataDir, "ensure_link_2");
+ const testFile = path.join(testDir, "test.txt");
+ const linkFile = path.join(testDir, "link.txt");
+
+ assertThrows(() => {
+ ensureLinkSync(testFile, linkFile);
+ });
+
+ Deno.removeSync(testDir, { recursive: true });
+});
+
+test(async function ensureLinkIfItExist() {
+ const testDir = path.join(testdataDir, "ensure_link_3");
+ const testFile = path.join(testDir, "test.txt");
+ const linkFile = path.join(testDir, "link.txt");
+
+ await Deno.mkdir(testDir, true);
+ await Deno.writeFile(testFile, new Uint8Array());
+
+ await ensureLink(testFile, linkFile);
+
+ const srcStat = await Deno.lstat(testFile);
+ const linkStat = await Deno.lstat(linkFile);
+
+ assertEquals(srcStat.isFile(), true);
+ assertEquals(linkStat.isFile(), true);
+
+ // har link success. try to change one of them. they should be change both.
+
+ // let's change origin file.
+ await Deno.writeFile(testFile, new TextEncoder().encode("123"));
+
+ const testFileContent1 = new TextDecoder().decode(
+ await Deno.readFile(testFile)
+ );
+ const linkFileContent1 = new TextDecoder().decode(
+ await Deno.readFile(testFile)
+ );
+
+ assertEquals(testFileContent1, "123");
+ assertEquals(testFileContent1, linkFileContent1);
+
+ // let's change link file.
+ await Deno.writeFile(testFile, new TextEncoder().encode("abc"));
+
+ const testFileContent2 = new TextDecoder().decode(
+ await Deno.readFile(testFile)
+ );
+ const linkFileContent2 = new TextDecoder().decode(
+ await Deno.readFile(testFile)
+ );
+
+ assertEquals(testFileContent2, "abc");
+ assertEquals(testFileContent2, linkFileContent2);
+
+ await Deno.remove(testDir, { recursive: true });
+});
+
+test(function ensureLinkSyncIfItExist() {
+ const testDir = path.join(testdataDir, "ensure_link_4");
+ const testFile = path.join(testDir, "test.txt");
+ const linkFile = path.join(testDir, "link.txt");
+
+ Deno.mkdirSync(testDir, true);
+ Deno.writeFileSync(testFile, new Uint8Array());
+
+ ensureLinkSync(testFile, linkFile);
+
+ const srcStat = Deno.lstatSync(testFile);
+
+ const linkStat = Deno.lstatSync(linkFile);
+
+ assertEquals(srcStat.isFile(), true);
+ assertEquals(linkStat.isFile(), true);
+
+ // har link success. try to change one of them. they should be change both.
+
+ // let's change origin file.
+ Deno.writeFileSync(testFile, new TextEncoder().encode("123"));
+
+ const testFileContent1 = new TextDecoder().decode(
+ Deno.readFileSync(testFile)
+ );
+ const linkFileContent1 = new TextDecoder().decode(
+ Deno.readFileSync(testFile)
+ );
+
+ assertEquals(testFileContent1, "123");
+ assertEquals(testFileContent1, linkFileContent1);
+
+ // let's change link file.
+ Deno.writeFileSync(testFile, new TextEncoder().encode("abc"));
+
+ const testFileContent2 = new TextDecoder().decode(
+ Deno.readFileSync(testFile)
+ );
+ const linkFileContent2 = new TextDecoder().decode(
+ Deno.readFileSync(testFile)
+ );
+
+ assertEquals(testFileContent2, "abc");
+ assertEquals(testFileContent2, linkFileContent2);
+
+ Deno.removeSync(testDir, { recursive: true });
+});
+
+test(async function ensureLinkDirectoryIfItExist() {
+ const testDir = path.join(testdataDir, "ensure_link_origin_3");
+ const linkDir = path.join(testdataDir, "ensure_link_link_3");
+ const testFile = path.join(testDir, "test.txt");
+
+ await Deno.mkdir(testDir, true);
+ await Deno.writeFile(testFile, new Uint8Array());
+
+ await assertThrowsAsync(
+ async () => {
+ await ensureLink(testDir, linkDir);
+ },
+ Deno.DenoError,
+ "Operation not permitted (os error 1)"
+ );
+
+ Deno.removeSync(testDir, { recursive: true });
+});
+
+test(function ensureLinkSyncDirectoryIfItExist() {
+ const testDir = path.join(testdataDir, "ensure_link_origin_3");
+ const linkDir = path.join(testdataDir, "ensure_link_link_3");
+ const testFile = path.join(testDir, "test.txt");
+
+ Deno.mkdirSync(testDir, true);
+ Deno.writeFileSync(testFile, new Uint8Array());
+
+ assertThrows(
+ () => {
+ ensureLinkSync(testDir, linkDir);
+ },
+ Deno.DenoError,
+ "Operation not permitted (os error 1)"
+ );
+
+ Deno.removeSync(testDir, { recursive: true });
+});
diff --git a/fs/mod.ts b/fs/mod.ts
index f9f8f8109..8635911a4 100644
--- a/fs/mod.ts
+++ b/fs/mod.ts
@@ -2,6 +2,7 @@
export * from "./empty_dir.ts";
export * from "./ensure_dir.ts";
export * from "./ensure_file.ts";
+export * from "./ensure_link.ts";
export * from "./ensure_symlink.ts";
export * from "./exists.ts";
export * from "./glob.ts";