diff --git a/compress_decompress/compress.js b/compress_decompress/compress.js new file mode 100644 index 0000000..241499f --- /dev/null +++ b/compress_decompress/compress.js @@ -0,0 +1,21 @@ +import { createReadStream, createWriteStream } from "node:fs" +import path from "node:path" +import { createBrotliCompress } from "node:zlib" +import { getFileFromPath } from "../utils/getFileFromPath.js" +import { printCWD } from "../utils/printCWD.js" + +export function compressFile(paths) { + const fileToCompress = getFileFromPath(paths[0]) + + const input = createReadStream(paths[0]) + + const brotli = createBrotliCompress() + + input.on("error", error => {console.error("Operation failed"); printCWD()}) + .pipe(brotli.on("error", error => {console.error("Operation failed"); printCWD()})) + .on("data", data => createWriteStream(path.join(paths[1], `${fileToCompress}.br`)) + .on("error", error => {console.error("Operation failed"); printCWD()}) + .on("open", () => printCWD()) + .write(data) + ) +} \ No newline at end of file diff --git a/compress_decompress/decompress.js b/compress_decompress/decompress.js new file mode 100644 index 0000000..f0efb89 --- /dev/null +++ b/compress_decompress/decompress.js @@ -0,0 +1,23 @@ +import { createReadStream, createWriteStream } from "node:fs" +import { createBrotliDecompress } from "node:zlib" +import path from "node:path" +import { getFileFromPath } from "../utils/getFileFromPath.js" +import { printCWD } from "../utils/printCWD.js" + +export function decompressFile(paths) { + + const fileToDecompress = getFileFromPath(paths[0]).split(".") + fileToDecompress.pop() // Remove .br extension + + const input = createReadStream(paths[0]) + + const brotli = createBrotliDecompress() + + input.on("error", error => {console.error("Operation failed"); printCWD()}) + .pipe(brotli.on("error", error => {console.error("Operation failed"); printCWD()})) + .on("data", data => createWriteStream(path.join(paths[1], fileToDecompress.join("."))) + .on("error", error => console.error("Operation failed")) + .on("open", () => printCWD()) + .write(data) + ) +} \ No newline at end of file diff --git a/compress_decompress/handleCompDecomp.js b/compress_decompress/handleCompDecomp.js new file mode 100644 index 0000000..fed41bb --- /dev/null +++ b/compress_decompress/handleCompDecomp.js @@ -0,0 +1,16 @@ +import { compressFile } from "./compress.js"; +import { decompressFile } from "./decompress.js"; + +export function handleCompDecomp(command, paths) { + switch (command) { + case "compress": + compressFile(paths) + break; + case "decompress": + decompressFile(paths) + break; + default: + console.error("Invalid input") + break; + } +} \ No newline at end of file diff --git a/file_operations/add.js b/file_operations/add.js new file mode 100644 index 0000000..338edac --- /dev/null +++ b/file_operations/add.js @@ -0,0 +1,11 @@ +import { writeFile} from "node:fs/promises" +import { printCWD } from "../utils/printCWD.js" + +export async function addFile(path) { + try { + await writeFile(path, "", {encoding: "utf8"}) + } catch (error) { + console.error("Operation failed") + } + printCWD() +} \ No newline at end of file diff --git a/file_operations/cat.js b/file_operations/cat.js new file mode 100644 index 0000000..87d104f --- /dev/null +++ b/file_operations/cat.js @@ -0,0 +1,8 @@ +import { createReadStream } from "node:fs" +import { printCWD } from "../utils/printCWD.js" + +export function readContent(path) { + createReadStream(path) + .on("error", error => console.error("Operation failed")) + .on("data", data => {console.log(data.toString()); printCWD()}) +} \ No newline at end of file diff --git a/file_operations/copy.js b/file_operations/copy.js new file mode 100644 index 0000000..0131370 --- /dev/null +++ b/file_operations/copy.js @@ -0,0 +1,33 @@ +import { createReadStream, createWriteStream, existsSync } from "node:fs" +import { printCWD } from "../utils/printCWD.js" +import path from "node:path" +import { mkdir } from "node:fs/promises" +import { getFileFromPath } from "../utils/getFileFromPath.js" +import { rm } from "node:fs/promises" + +export async function copyFile(paths, withRemove = false) { + try { + if (!existsSync(paths[1])) { + await mkdir(paths[1], { recursive: true }) + } + + const fileToCopy = getFileFromPath(paths[0]) + + const dest = path.join(paths[1], fileToCopy) + + createReadStream(paths[0], { encoding: "utf8" }) + .on("error", error => {console.error("Operation failed"); printCWD()}) + .on("data", data => { + createWriteStream(dest) + .on("error", error => console.error("Operation failed")) + .on("open", async () => withRemove + ? (await rm(paths[0]).catch(error => console.error("Operation failed")), printCWD()) + : printCWD() + ) + .write(data) + }) + } catch (error) { + console.error("Operation failed") + } + +} \ No newline at end of file diff --git a/file_operations/handleOperations.js b/file_operations/handleOperations.js new file mode 100644 index 0000000..f07d291 --- /dev/null +++ b/file_operations/handleOperations.js @@ -0,0 +1,31 @@ +import { readContent } from "./cat.js"; +import { addFile } from "./add.js"; +import { renameFile } from "./rename.js"; +import { copyFile } from "./copy.js"; +import { removeFile } from "./remove.js"; + +export async function handleFileOperation(operation, paths) { + switch (operation) { + case "cat": + readContent(paths[0]) + break; + case "add": + await addFile(paths[0]) + break; + case "rn": + await renameFile(paths) + break; + case "cp": + await copyFile(paths) + break; + case "mv": + await copyFile(paths, true) + break; + case "rm": + await removeFile(paths[0]) + break; + default: + console.error("Invalid input") + break; + } +} \ No newline at end of file diff --git a/file_operations/remove.js b/file_operations/remove.js new file mode 100644 index 0000000..4d3d781 --- /dev/null +++ b/file_operations/remove.js @@ -0,0 +1,7 @@ +import { rm } from "node:fs/promises" +import { printCWD } from "../utils/printCWD.js" + +export async function removeFile(path) { + await rm(path).catch(error => console.error("Operation failed")) + printCWD() +} \ No newline at end of file diff --git a/file_operations/rename.js b/file_operations/rename.js new file mode 100644 index 0000000..46dbfaf --- /dev/null +++ b/file_operations/rename.js @@ -0,0 +1,12 @@ +import { rename } from "node:fs/promises" +import { printCWD } from "../utils/printCWD.js" +import { getFileFromPath } from "../utils/getFileFromPath.js" +import path from "node:path" + +export async function renameFile(paths) { + const fileNameLength = getFileFromPath(paths[0]).length + const pathToFile = paths[0].slice(0, paths[0].length - fileNameLength) + await rename(paths[0], path.join(pathToFile, paths[1])) + .catch(error => console.error("Operation failed")) + printCWD() +} \ No newline at end of file diff --git a/hash/handleHashCommand.js b/hash/handleHashCommand.js new file mode 100644 index 0000000..c28215a --- /dev/null +++ b/hash/handleHashCommand.js @@ -0,0 +1,12 @@ +import { createHash } from "node:crypto" +import { readFile } from "node:fs/promises" +import { printCWD } from "../utils/printCWD.js" + +export async function hashFile(path) { + try { + console.log(createHash("sha256").update(await readFile(path)).digest("hex")) + } catch (error) { + console.error("Operation failed") + } + printCWD() +} \ No newline at end of file diff --git a/index.js b/index.js index 2d41852..a666668 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,8 @@ import process from "node:process" import readline from "node:readline/promises" import { handleUserInput } from "./user_input/handleUserInput.js" - -export const printCWD = () => console.log(`You are currently in ${process.cwd()}`) +import { homedir } from "node:os" +import { printCWD } from "./utils/printCWD.js" async function fileManager() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) @@ -11,9 +11,18 @@ async function fileManager() { || await rl.question("Please, provide your username: ") console.log(`Welcome to the File Manager, ${username}!`) + + process.chdir(homedir()) + printCWD() - rl.on("line", async (line) => await handleUserInput(line)) + rl.on("line", + (line) => line === ".exit" || line === "CLOSE" + ? process.exit() + : handleUserInput(line.trim()) + ) + + process.on("exit", () => console.log(`Thank you for using File Manager, ${username}, goodbye!`)) } await fileManager() \ No newline at end of file diff --git a/os/handleOSCommands.js b/os/handleOSCommands.js new file mode 100644 index 0000000..91db03e --- /dev/null +++ b/os/handleOSCommands.js @@ -0,0 +1,24 @@ +import { EOL, cpus, homedir, userInfo } from "node:os" + +export function handleOSCommands(command) { + switch (command) { + case "--EOL": + console.log(`End of line: ${JSON.stringify(EOL)}`) + break; + case "--cpus": + console.log("Machine CPUs: ", cpus().map(cpu => cpu.model)) + break; + case "--homedir": + console.log(`Home directory: ${homedir}`) + break; + case "--username": + console.log(`System username: ${userInfo().username}`) + break; + case "--architecture": + console.log(`CPU architecture: ${process.arch}`) + break; + default: + console.error("Invalid input") + break; + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..6474b6b --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "file-manager", + "version": "1.0.0", + "description": "RS school file manager task", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "bulbex", + "license": "ISC" +} diff --git a/user_input/handleUserInput.js b/user_input/handleUserInput.js new file mode 100644 index 0000000..a1b508c --- /dev/null +++ b/user_input/handleUserInput.js @@ -0,0 +1,77 @@ +import { handleFileOperation } from "../file_operations/handleOperations.js" +import { handleOSCommands } from "../os/handleOSCommands.js" +import { printCWD } from "../utils/printCWD.js" +import { readdir } from "node:fs" +import { hashFile } from "../hash/handleHashCommand.js" +import { handleCompDecomp } from "../compress_decompress/handleCompDecomp.js" + +export async function handleUserInput(line) { + try { + if (line === "") { + printCWD() + return + } + + if (line === "up") { + process.chdir("..") + printCWD() + return + } + + if (line === "ls") { + readdir(process.cwd(), {withFileTypes: true}, + (err, files) => { + if (err) { + console.error("Operation failed") + return + } + console.table( + files.sort((a, b) => { + return a.isDirectory() + ? b.isDirectory() + ? a.name - b.name + : -1 + : b.isDirectory() + ? 1 + : a.name - b.name + }).map(file => Object.assign({ name: file.name, type: file.isDirectory() ? 'directory' : 'file' }))) + printCWD() + }) + return + } + + if (line.startsWith("cd ")) { + process.chdir(line.split(" ").slice(1).join(" ")) + printCWD() + return + } + + if (line.startsWith("os ")) { + handleOSCommands(line.split(" ")[1]) + printCWD() + return + } + + const command = line.split(" ")[0] + const filePaths = command.startsWith("cp") && line.split(" ").slice(1).length === 2 + ? line.split(" ").slice(1) + : line.slice(command.length).match(/[^ (\"|\')][\:*\w+ \/\.]+[^ (\"|\')]/g) + + if (line.startsWith("hash ")) { + await hashFile(filePaths[0]) + return + } + + + if (line.startsWith("compress ") || line.startsWith("decompress ")) { + handleCompDecomp(command, filePaths) + return + } + + await handleFileOperation(command, filePaths) + + } catch (error) { + console.error(error) + printCWD() + } +} \ No newline at end of file diff --git a/utils/getFileFromPath.js b/utils/getFileFromPath.js new file mode 100644 index 0000000..6be3416 --- /dev/null +++ b/utils/getFileFromPath.js @@ -0,0 +1,5 @@ +export function getFileFromPath(path) { + return path.includes("/") + ? `${path.split("/").slice(-1)}` + : `${path.split("\\").slice(-1)}` +} \ No newline at end of file diff --git a/utils/printCWD.js b/utils/printCWD.js new file mode 100644 index 0000000..ff651f2 --- /dev/null +++ b/utils/printCWD.js @@ -0,0 +1 @@ +export const printCWD = () => console.log(`You are currently in ${process.cwd()}`) \ No newline at end of file