From 19f6c279e3778bf9d299959b10fbfc47f2501cbe Mon Sep 17 00:00:00 2001 From: Andrey Shevtsov Date: Sat, 2 Dec 2023 18:11:47 -0700 Subject: [PATCH] Define basic command line interface 1. Rename the `downloader` module to `actions/download` with the intent of placing future implementations of CLI actions under the `actions` top levle module. 2. Use `commander` to define basic command line interface: * Default _no-name_ command to download all new files from a specified removable drive. * `init` command to initialize a global configuration for this tool. --- package-lock.json | 15 ++++++++++-- package.json | 4 +++- .../download}/downloader.ts | 6 ++--- .../download}/file-copier.ts | 0 src/actions/download/index.ts | 6 +++++ .../download}/placement-strategy.ts | 0 .../download}/progress.ts | 0 src/downloader/index.ts | 1 - src/index.ts | 24 ++++++++++++++++++- .../.resources/dummy-drive-1/DCIM/.gitkeep | 0 .../.resources/dummy-drive-2/DCIM/IMG001.ARW | 0 .../download}/.resources/empty-drive/.gitkeep | 0 .../download}/downloader.spec.ts | 4 ++-- .../download}/file-copier.spec.ts | 2 +- .../download}/placement-strategy.spec.ts | 2 +- 15 files changed, 52 insertions(+), 12 deletions(-) rename src/{downloader => actions/download}/downloader.ts (97%) rename src/{downloader => actions/download}/file-copier.ts (100%) create mode 100644 src/actions/download/index.ts rename src/{downloader => actions/download}/placement-strategy.ts (100%) rename src/{downloader => actions/download}/progress.ts (100%) delete mode 100644 src/downloader/index.ts rename test/{downloader => actions/download}/.resources/dummy-drive-1/DCIM/.gitkeep (100%) rename test/{downloader => actions/download}/.resources/dummy-drive-2/DCIM/IMG001.ARW (100%) rename test/{downloader => actions/download}/.resources/empty-drive/.gitkeep (100%) rename test/{downloader => actions/download}/downloader.spec.ts (89%) rename test/{downloader => actions/download}/file-copier.spec.ts (98%) rename test/{downloader => actions/download}/placement-strategy.spec.ts (94%) diff --git a/package-lock.json b/package-lock.json index b4cd968..9d55429 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,13 +9,15 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "cli-progress": "^3.12.0" + "cli-progress": "^3.12.0", + "commander": "^11.1.0" }, "devDependencies": { "@tsconfig/node20": "^20.1.2", "@types/chai": "^4.3.9", "@types/chai-as-promised": "^7.1.8", "@types/cli-progress": "^3.11.5", + "@types/commander": "^2.12.2", "@types/jest": "^29.5.8", "@types/node": "^20.8.10", "@typescript-eslint/eslint-plugin": "^6.9.1", @@ -1531,6 +1533,16 @@ "@types/node": "*" } }, + "node_modules/@types/commander": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@types/commander/-/commander-2.12.2.tgz", + "integrity": "sha512-0QEFiR8ljcHp9bAbWxecjVRuAMr16ivPiGOw6KFQBVrVd0RQIcM3xKdRisH2EDWgVWujiYtHwhSkSUoAAGzH7Q==", + "deprecated": "This is a stub types definition for commander (https://github.com/tj/commander.js). commander provides its own type definitions, so you don't need @types/commander installed!", + "dev": true, + "dependencies": { + "commander": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -2394,7 +2406,6 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, "engines": { "node": ">=16" } diff --git a/package.json b/package.json index 23d9b3e..a1d7cfd 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@types/chai": "^4.3.9", "@types/chai-as-promised": "^7.1.8", "@types/cli-progress": "^3.11.5", + "@types/commander": "^2.12.2", "@types/jest": "^29.5.8", "@types/node": "^20.8.10", "@typescript-eslint/eslint-plugin": "^6.9.1", @@ -50,6 +51,7 @@ "**/*": "prettier --write --ignore-unknown" }, "dependencies": { - "cli-progress": "^3.12.0" + "cli-progress": "^3.12.0", + "commander": "^11.1.0" } } diff --git a/src/downloader/downloader.ts b/src/actions/download/downloader.ts similarity index 97% rename from src/downloader/downloader.ts rename to src/actions/download/downloader.ts index af86998..ee347f3 100644 --- a/src/downloader/downloader.ts +++ b/src/actions/download/downloader.ts @@ -4,9 +4,9 @@ import { DirectoryDownloadConfig, DriveDownloadConfiguration, readAutoDownloadConfiguration, -} from "../configuration"; -import { SequentialNamingScanner } from "../scanner"; -import { readDirectoryCursor, saveDirectoryCursor } from "../source-cursor"; +} from "../../configuration"; +import { SequentialNamingScanner } from "../../scanner"; +import { readDirectoryCursor, saveDirectoryCursor } from "../../source-cursor"; import { FileCopier } from "./file-copier"; import { GroupByDatePlacementStrategy, TargetPlacementStrategy } from "./placement-strategy"; import { ProgressTracker } from "./progress"; diff --git a/src/downloader/file-copier.ts b/src/actions/download/file-copier.ts similarity index 100% rename from src/downloader/file-copier.ts rename to src/actions/download/file-copier.ts diff --git a/src/actions/download/index.ts b/src/actions/download/index.ts new file mode 100644 index 0000000..ffac92f --- /dev/null +++ b/src/actions/download/index.ts @@ -0,0 +1,6 @@ +// Note: consilder all sub-modules to be 'private' to this module and do not re-export any of them here. +import { Downloader } from "./downloader"; + +export async function downloadAllNewFiles(drivePath: string) { + await new Downloader().downloadAllNewFiles(drivePath); +} diff --git a/src/downloader/placement-strategy.ts b/src/actions/download/placement-strategy.ts similarity index 100% rename from src/downloader/placement-strategy.ts rename to src/actions/download/placement-strategy.ts diff --git a/src/downloader/progress.ts b/src/actions/download/progress.ts similarity index 100% rename from src/downloader/progress.ts rename to src/actions/download/progress.ts diff --git a/src/downloader/index.ts b/src/downloader/index.ts deleted file mode 100644 index f8d7443..0000000 --- a/src/downloader/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./downloader"; diff --git a/src/index.ts b/src/index.ts index 940a3ff..1a78236 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,23 @@ -console.log("Hello world!"); +import { program } from "commander"; +import { downloadAllNewFiles } from "./actions/download"; + +program + .command("init") + .description("Initialize a global configuration") + .action(async () => { + // TODO: initialize configuration. + throw new Error("Not implemented yet"); + }); + +program + .description( + `Download all new files from the specified removable drive (e.g. a memory card). +Relies on the global configuration to identify supported directories on the removable drive and to determine where their contents should be saved.` + ) + // This removes `[command]` from the default no-name command usage sample to avoid the implication + // that explicit commands (i.e. other than the default no-name command) require the `drive-path` argument. + .usage("") + .argument("", "Removable drive (e.g. memory card) path") + .action(downloadAllNewFiles); + +program.parse(); diff --git a/test/downloader/.resources/dummy-drive-1/DCIM/.gitkeep b/test/actions/download/.resources/dummy-drive-1/DCIM/.gitkeep similarity index 100% rename from test/downloader/.resources/dummy-drive-1/DCIM/.gitkeep rename to test/actions/download/.resources/dummy-drive-1/DCIM/.gitkeep diff --git a/test/downloader/.resources/dummy-drive-2/DCIM/IMG001.ARW b/test/actions/download/.resources/dummy-drive-2/DCIM/IMG001.ARW similarity index 100% rename from test/downloader/.resources/dummy-drive-2/DCIM/IMG001.ARW rename to test/actions/download/.resources/dummy-drive-2/DCIM/IMG001.ARW diff --git a/test/downloader/.resources/empty-drive/.gitkeep b/test/actions/download/.resources/empty-drive/.gitkeep similarity index 100% rename from test/downloader/.resources/empty-drive/.gitkeep rename to test/actions/download/.resources/empty-drive/.gitkeep diff --git a/test/downloader/downloader.spec.ts b/test/actions/download/downloader.spec.ts similarity index 89% rename from test/downloader/downloader.spec.ts rename to test/actions/download/downloader.spec.ts index 4bd42ab..277f4dd 100644 --- a/test/downloader/downloader.spec.ts +++ b/test/actions/download/downloader.spec.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; -import { DirectoryDownloadConfig } from "../../src/configuration"; -import { findAllSupportedSourceDirs } from "../../src/downloader"; +import { DirectoryDownloadConfig } from "../../../src/configuration"; +import { findAllSupportedSourceDirs } from "../../../src/actions/download/downloader"; describe("findAllSupportedSourceDirs", () => { const testConfiguration = { diff --git a/test/downloader/file-copier.spec.ts b/test/actions/download/file-copier.spec.ts similarity index 98% rename from test/downloader/file-copier.spec.ts rename to test/actions/download/file-copier.spec.ts index 8dd55e4..ceefc3f 100644 --- a/test/downloader/file-copier.spec.ts +++ b/test/actions/download/file-copier.spec.ts @@ -1,6 +1,6 @@ import * as fs from "node:fs/promises"; import * as os from "node:os"; -import { FileCopier } from "../../src/downloader/file-copier"; +import { FileCopier } from "../../../src/actions/download/file-copier"; jest.mock("node:fs/promises"); diff --git a/test/downloader/placement-strategy.spec.ts b/test/actions/download/placement-strategy.spec.ts similarity index 94% rename from test/downloader/placement-strategy.spec.ts rename to test/actions/download/placement-strategy.spec.ts index cede33a..ed741fd 100644 --- a/test/downloader/placement-strategy.spec.ts +++ b/test/actions/download/placement-strategy.spec.ts @@ -1,6 +1,6 @@ import { Stats } from "node:fs"; import * as fs from "node:fs/promises"; -import { GroupByDatePlacementStrategy } from "../../src/downloader/placement-strategy"; +import { GroupByDatePlacementStrategy } from "../../../src/actions/download/placement-strategy"; jest.mock("node:fs/promises");