From 511dbdde73bec2d7096e77e5a20fc697b007df3f Mon Sep 17 00:00:00 2001 From: Axetroy Date: Tue, 12 Mar 2019 17:11:30 +0800 Subject: [PATCH] feat: add move/moveSync for fs modules (denoland/deno_std#266) Original: https://github.com/denoland/deno_std/commit/a1fcfb2744a7a2c660bf03b8da03ca2c4d7753a8 --- fs/move.ts | 88 +++++++++++++ fs/move_test.ts | 324 ++++++++++++++++++++++++++++++++++++++++++++++++ test.ts | 1 + 3 files changed, 413 insertions(+) create mode 100644 fs/move.ts create mode 100644 fs/move_test.ts diff --git a/fs/move.ts b/fs/move.ts new file mode 100644 index 0000000000..f645097886 --- /dev/null +++ b/fs/move.ts @@ -0,0 +1,88 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import * as path from "./path/mod.ts"; +import { exists, existsSync } from "./exists.ts"; + +interface MoveOptions { + overwrite?: boolean; +} + +function isSrcSubdir(src: string, dest: string): boolean { + const srcArray = src.split(path.sep); + const destArray = dest.split(path.sep); + + return srcArray.reduce((acc, current, i) => { + return acc && destArray[i] === current; + }, true); +} + +/** + * Moves a file or directory + * @export + * @param {string} src + * @param {string} dest + * @param {MoveOptions} [options] + * @returns {Promise} + */ +export async function move( + src: string, + dest: string, + options?: MoveOptions +): Promise { + src = path.resolve(src); + dest = path.resolve(dest); + + const srcStat = await Deno.stat(src); + + if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { + throw new Error( + `Cannot move '${src}' to a subdirectory of itself, '${dest}'.` + ); + } + + if (options && options.overwrite) { + await Deno.remove(dest, { recursive: true }); + await Deno.rename(src, dest); + } else { + if (await exists(dest)) { + throw new Error("dest already exists."); + } + await Deno.rename(src, dest); + } + + return; +} + +/** + * Moves a file or directory + * @export + * @param {string} src + * @param {string} dest + * @param {MoveOptions} [options] + * @returns {void} + */ +export function moveSync( + src: string, + dest: string, + options?: MoveOptions +): void { + src = path.resolve(src); + dest = path.resolve(dest); + + const srcStat = Deno.statSync(src); + + if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { + throw new Error( + `Cannot move '${src}' to a subdirectory of itself, '${dest}'.` + ); + } + + if (options && options.overwrite) { + Deno.removeSync(dest, { recursive: true }); + Deno.renameSync(src, dest); + } else { + if (existsSync(dest)) { + throw new Error("dest already exists."); + } + Deno.renameSync(src, dest); + } +} diff --git a/fs/move_test.ts b/fs/move_test.ts new file mode 100644 index 0000000000..f8d7a6f447 --- /dev/null +++ b/fs/move_test.ts @@ -0,0 +1,324 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { + assertEquals, + assertThrows, + assertThrowsAsync +} from "../testing/asserts.ts"; +import { move, moveSync } from "./move.ts"; +import { ensureFile, ensureFileSync } from "./ensure_file.ts"; +import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; +import { exists, existsSync } from "./exists.ts"; +import * as path from "./path/mod.ts"; + +const testdataDir = path.resolve("fs", "testdata"); + +test(async function moveDirectoryIfSrcNotExists() { + const srcDir = path.join(testdataDir, "move_test_src_1"); + const destDir = path.join(testdataDir, "move_test_dest_1"); + // if src directory not exist + await assertThrowsAsync(async () => { + await move(srcDir, destDir); + }); +}); + +test(async function moveDirectoryIfDestNotExists() { + const srcDir = path.join(testdataDir, "move_test_src_2"); + const destDir = path.join(testdataDir, "move_test_dest_2"); + + await Deno.mkdir(srcDir, true); + + // if dest directory not exist + await assertThrowsAsync( + async () => { + await move(srcDir, destDir); + throw new Error("should not throw error"); + }, + Error, + "should not throw error" + ); + + await Deno.remove(destDir); +}); + +test(async function moveFileIfSrcNotExists() { + const srcFile = path.join(testdataDir, "move_test_src_3", "test.txt"); + const destFile = path.join(testdataDir, "move_test_dest_3", "test.txt"); + + // if src directory not exist + await assertThrowsAsync(async () => { + await move(srcFile, destFile); + }); +}); + +test(async function moveFileIfDestExists() { + const srcDir = path.join(testdataDir, "move_test_src_4"); + const destDir = path.join(testdataDir, "move_test_dest_4"); + const srcFile = path.join(srcDir, "test.txt"); + const destFile = path.join(destDir, "test.txt"); + const srcContent = new TextEncoder().encode("src"); + const destContent = new TextEncoder().encode("dest"); + + // make sure files exists + await Promise.all([ensureFile(srcFile), ensureFile(destFile)]); + + // write file content + await Promise.all([ + Deno.writeFile(srcFile, srcContent), + Deno.writeFile(destFile, destContent) + ]); + + // make sure the test file have been created + assertEquals(new TextDecoder().decode(await Deno.readFile(srcFile)), "src"); + assertEquals(new TextDecoder().decode(await Deno.readFile(destFile)), "dest"); + + // move it without override + await assertThrowsAsync( + async () => { + await move(srcFile, destFile); + }, + Error, + "dest already exists" + ); + + // move again with overwrite + await assertThrowsAsync( + async () => { + await move(srcFile, destFile, { overwrite: true }); + throw new Error("should not throw error"); + }, + Error, + "should not throw error" + ); + + assertEquals(await exists(srcFile), false); + assertEquals(new TextDecoder().decode(await Deno.readFile(destFile)), "src"); + + // clean up + await Promise.all([ + Deno.remove(srcDir, { recursive: true }), + Deno.remove(destDir, { recursive: true }) + ]); +}); + +test(async function moveDirectory() { + const srcDir = path.join(testdataDir, "move_test_src_5"); + const destDir = path.join(testdataDir, "move_test_dest_5"); + const srcFile = path.join(srcDir, "test.txt"); + const destFile = path.join(destDir, "test.txt"); + const srcContent = new TextEncoder().encode("src"); + + await Deno.mkdir(srcDir, true); + assertEquals(await exists(srcDir), true); + await Deno.writeFile(srcFile, srcContent); + + await move(srcDir, destDir); + + assertEquals(await exists(srcDir), false); + assertEquals(await exists(destDir), true); + assertEquals(await exists(destFile), true); + + const destFileContent = new TextDecoder().decode( + await Deno.readFile(destFile) + ); + assertEquals(destFileContent, "src"); + + await Deno.remove(destDir, { recursive: true }); +}); + +test(async function moveIfSrcAndDestDirectoryExistsAndOverwrite() { + const srcDir = path.join(testdataDir, "move_test_src_6"); + const destDir = path.join(testdataDir, "move_test_dest_6"); + const srcFile = path.join(srcDir, "test.txt"); + const destFile = path.join(destDir, "test.txt"); + const srcContent = new TextEncoder().encode("src"); + const destContent = new TextEncoder().encode("dest"); + + await Promise.all([Deno.mkdir(srcDir, true), Deno.mkdir(destDir, true)]); + assertEquals(await exists(srcDir), true); + assertEquals(await exists(destDir), true); + await Promise.all([ + Deno.writeFile(srcFile, srcContent), + Deno.writeFile(destFile, destContent) + ]); + + await move(srcDir, destDir, { overwrite: true }); + + assertEquals(await exists(srcDir), false); + assertEquals(await exists(destDir), true); + assertEquals(await exists(destFile), true); + + const destFileContent = new TextDecoder().decode( + await Deno.readFile(destFile) + ); + assertEquals(destFileContent, "src"); + + await Deno.remove(destDir, { recursive: true }); +}); + +test(async function moveIntoSubDir() { + const srcDir = path.join(testdataDir, "move_test_src_7"); + const destDir = path.join(srcDir, "nest"); + + await ensureDir(destDir); + + await assertThrowsAsync( + async () => { + await move(srcDir, destDir); + }, + Error, + `Cannot move '${srcDir}' to a subdirectory of itself, '${destDir}'.` + ); + await Deno.remove(srcDir, { recursive: true }); +}); + +test(function moveSyncDirectoryIfSrcNotExists() { + const srcDir = path.join(testdataDir, "move_sync_test_src_1"); + const destDir = path.join(testdataDir, "move_sync_test_dest_1"); + // if src directory not exist + assertThrows(() => { + moveSync(srcDir, destDir); + }); +}); + +test(function moveSyncDirectoryIfDestNotExists() { + const srcDir = path.join(testdataDir, "move_sync_test_src_2"); + const destDir = path.join(testdataDir, "move_sync_test_dest_2"); + + Deno.mkdirSync(srcDir, true); + + // if dest directory not exist + assertThrows( + () => { + moveSync(srcDir, destDir); + throw new Error("should not throw error"); + }, + Error, + "should not throw error" + ); + + Deno.removeSync(destDir); +}); + +test(function moveSyncFileIfSrcNotExists() { + const srcFile = path.join(testdataDir, "move_sync_test_src_3", "test.txt"); + const destFile = path.join(testdataDir, "move_sync_test_dest_3", "test.txt"); + + // if src directory not exist + assertThrows(() => { + moveSync(srcFile, destFile); + }); +}); + +test(function moveSyncFileIfDestExists() { + const srcDir = path.join(testdataDir, "move_sync_test_src_4"); + const destDir = path.join(testdataDir, "move_sync_test_dest_4"); + const srcFile = path.join(srcDir, "test.txt"); + const destFile = path.join(destDir, "test.txt"); + const srcContent = new TextEncoder().encode("src"); + const destContent = new TextEncoder().encode("dest"); + + // make sure files exists + ensureFileSync(srcFile); + ensureFileSync(destFile); + + // write file content + Deno.writeFileSync(srcFile, srcContent); + Deno.writeFileSync(destFile, destContent); + + // make sure the test file have been created + assertEquals(new TextDecoder().decode(Deno.readFileSync(srcFile)), "src"); + assertEquals(new TextDecoder().decode(Deno.readFileSync(destFile)), "dest"); + + // move it without override + assertThrows( + () => { + moveSync(srcFile, destFile); + }, + Error, + "dest already exists" + ); + + // move again with overwrite + assertThrowsAsync( + () => { + moveSync(srcFile, destFile, { overwrite: true }); + throw new Error("should not throw error"); + }, + Error, + "should not throw error" + ); + + assertEquals(existsSync(srcFile), false); + assertEquals(new TextDecoder().decode(Deno.readFileSync(destFile)), "src"); + + // clean up + Deno.removeSync(srcDir, { recursive: true }); + Deno.removeSync(destDir, { recursive: true }); +}); + +test(function moveSyncDirectory() { + const srcDir = path.join(testdataDir, "move_sync_test_src_5"); + const destDir = path.join(testdataDir, "move_sync_test_dest_5"); + const srcFile = path.join(srcDir, "test.txt"); + const destFile = path.join(destDir, "test.txt"); + const srcContent = new TextEncoder().encode("src"); + + Deno.mkdirSync(srcDir, true); + assertEquals(existsSync(srcDir), true); + Deno.writeFileSync(srcFile, srcContent); + + moveSync(srcDir, destDir); + + assertEquals(existsSync(srcDir), false); + assertEquals(existsSync(destDir), true); + assertEquals(existsSync(destFile), true); + + const destFileContent = new TextDecoder().decode(Deno.readFileSync(destFile)); + assertEquals(destFileContent, "src"); + + Deno.removeSync(destDir, { recursive: true }); +}); + +test(function moveSyncIfSrcAndDestDirectoryExistsAndOverwrite() { + const srcDir = path.join(testdataDir, "move_sync_test_src_6"); + const destDir = path.join(testdataDir, "move_sync_test_dest_6"); + const srcFile = path.join(srcDir, "test.txt"); + const destFile = path.join(destDir, "test.txt"); + const srcContent = new TextEncoder().encode("src"); + const destContent = new TextEncoder().encode("dest"); + + Deno.mkdirSync(srcDir, true); + Deno.mkdirSync(destDir, true); + assertEquals(existsSync(srcDir), true); + assertEquals(existsSync(destDir), true); + Deno.writeFileSync(srcFile, srcContent); + Deno.writeFileSync(destFile, destContent); + + moveSync(srcDir, destDir, { overwrite: true }); + + assertEquals(existsSync(srcDir), false); + assertEquals(existsSync(destDir), true); + assertEquals(existsSync(destFile), true); + + const destFileContent = new TextDecoder().decode(Deno.readFileSync(destFile)); + assertEquals(destFileContent, "src"); + + Deno.removeSync(destDir, { recursive: true }); +}); + +test(function moveSyncIntoSubDir() { + const srcDir = path.join(testdataDir, "move_sync_test_src_7"); + const destDir = path.join(srcDir, "nest"); + + ensureDirSync(destDir); + + assertThrows( + () => { + moveSync(srcDir, destDir); + }, + Error, + `Cannot move '${srcDir}' to a subdirectory of itself, '${destDir}'.` + ); + Deno.removeSync(srcDir, { recursive: true }); +}); diff --git a/test.ts b/test.ts index a8f29bc990..3c27e1c4ac 100755 --- a/test.ts +++ b/test.ts @@ -17,6 +17,7 @@ import "./fs/exists_test.ts"; import "./fs/empty_dir_test.ts"; import "./fs/ensure_dir_test.ts"; import "./fs/ensure_file_test.ts"; +import "./fs/move_test.ts"; import "./io/test.ts"; import "./http/server_test.ts"; import "./http/file_server_test.ts";