Created
August 21, 2024 20:06
-
-
Save cblair/0dd50282951ec616f23c0e2d033edad8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// WIP | |
// Objective: Create a Node.js script that encrypts text files within a specified directory using asynchronous programming. This script should encrypt the content of each text file using a basic Caesar Cipher for simplicity, then save the encrypted content to a new file in a different directory. The process should handle multiple files concurrently, support both encryption and decryption modes, and log the completion of each file operation. | |
// Requirements: | |
// Mode Selection: The script should accept a mode argument (encrypt or decrypt) as the first command-line argument to determine the operation mode. | |
// Directory Arguments: Accept two additional command-line arguments for the input directory (containing text files to process) and the output directory (where encrypted or decrypted files will be saved). | |
const { assert } = require("node:console"); | |
const { argv } = require("node:process"); | |
const { readdir, readFile } = require('node:fs/promises'); | |
// Requirements: | |
// Asynchronous Processing: Utilize Node.js's fs.promises module for asynchronous file operations to ensure non-blocking I/O. | |
// Caesar Cipher Transformation: Implement a simple Caesar Cipher for text transformation. For encryption, shift each letter by a fixed number (e.g., 3 positions forward). For decryption, reverse the shift. Non-letter characters remain unchanged. | |
// Concurrency: Process all files found in the input directory concurrently, taking advantage of Node.js's asynchronous capabilities. | |
// Logging: Upon completion of processing a file, log a message indicating the file name and whether it was encrypted or decrypted. | |
class Encrypt { | |
iDir = null; | |
oDir = null; | |
iFilePaths = []; | |
numToShift = 3; | |
letterRE = new RegExp("[a-zA-Z]+"); | |
constructor(iDir, oDir) { | |
this.iDir = iDir; | |
this.oDir = oDir; | |
} | |
async init() { | |
try { | |
this.iFilePaths = await readdir(this.iDir); | |
this.iFilePaths = this.iFilePaths.map(fname => `${this.iDir}${fname}`); | |
} catch (err) { | |
console.error(err); | |
return; | |
} | |
} | |
processInputFiles(mode) { | |
this.iFilePaths.forEach(fname => { | |
// We are assuming file of small enough size to load all in memory. When that changes, then readlines instead. | |
readFile(fname, 'utf-8') | |
.then(contents => { | |
if (mode === 'encrypt') { | |
this.encryptFile(fname, contents); | |
} else if (mode === 'decrypt') { | |
// | |
} else { | |
console.error("mode was not a correct value.") | |
} | |
}) | |
.catch(err => console.error('Could not read file, error: ' + err.message)); | |
}); | |
} | |
// We're assuming file size is small enough to load all in memory for now, for simplicity. | |
encryptFile(fname, content) { | |
const encryptedContent = content.split('').map(char => { | |
const c = this.ceaserShiftChar(char, this.numToShift); | |
return c; | |
}).join(''); | |
const testDecrypt = content.split('').map(char => { | |
const c = this.ceaserShiftChar(char, -this.numToShift); | |
return c; | |
}).join(''); | |
} | |
// decryptFile(fname) | |
// Caesar Cipher Transformation: Implement a simple Caesar Cipher for text transformation. For encryption, shift each letter by a fixed number (e.g., 3 positions forward). For decryption, reverse the shift. Non-letter characters remain unchanged. | |
ceaserShiftChar(char, numOfShifts) { | |
const isCharLetter = this.letterRE.test(); | |
let result = char; | |
if (isCharLetter) { | |
// a = 97, z = 122, A = 65, Z = 90 | |
let charCode = char.charCodeAt(0) | |
// Process upper chars | |
if (charCode >= 65 && charCode <= 90) { | |
let newCharCode = charCode + this.numToShift; | |
// Overflow the end | |
if (newCharCode > 90) { | |
newCharCode = (newCharCode % 90) + 65; | |
} | |
// Underflow the begining | |
result = String.fromCharCode(newCharCode); | |
} | |
// Process lower chars | |
else if (charCode >= 97 && charCode <= 122) { | |
let newCharCode = charCode + this.numToShift; | |
// Overflow | |
if (newCharCode > 122) { | |
newCharCode = (newCharCode % 122) + 97; | |
} | |
result = String.fromCharCode(newCharCode); | |
} | |
} | |
return result; | |
} | |
} | |
// Args | |
const args = {}; | |
argv.forEach((argument, index) => { | |
if (index === 2) { | |
args['mode'] = argument; | |
} else if(index === 3) { | |
args['iDir'] = argument; | |
} else if (index === 4) { | |
args['oDir'] = argument; | |
} | |
}); | |
assert(args['mode'] !== undefined); | |
assert(args['iDir'] !== undefined); | |
assert(args['oDir'] !== undefined); | |
const encrypt = new Encrypt(args.iDir, args.oDir); | |
// Run main | |
(async function main() { | |
await encrypt.init(); | |
encrypt.processInputFiles(args['mode']); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment