Skip to content

Commit

Permalink
Applied more/stricter SonarQube rules
Browse files Browse the repository at this point in the history
  • Loading branch information
david-04 committed May 26, 2022
1 parent 156e76a commit c32933f
Show file tree
Hide file tree
Showing 29 changed files with 446 additions and 300 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## [1.0.3](https://github.com/david-04/7-sync/releases/tag/v1.0.3) (2022-05-27)

- Technical maintenance (satisfy more SonarQube linting rules)

## [1.0.2](https://github.com/david-04/7-sync/releases/tag/v1.0.2) (2022-04-23)

- Avoid Windows reserved filenames (e.g. `PRN`, `AUX`, `NUL`, `COM1`, ...)
Expand Down
275 changes: 162 additions & 113 deletions package/7-sync.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "7-sync",
"version": "1.0.2",
"version": "1.0.3",
"description": "A wrapper for 7-Zip to create cloud-friendly encrypted backups",
"main": "index.js",
"repository": {
Expand Down
6 changes: 4 additions & 2 deletions src/data/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class Context {

private static readonly MAX_LOG_FILES = 9;

public readonly print;

//------------------------------------------------------------------------------------------------------------------
Expand All @@ -13,7 +15,7 @@ class Context {
private constructor(
public readonly options: SyncOptions,
public readonly config: JsonConfig,
public readonly files: { config: string, log: string },
public readonly files: { config: string, log: string; },
public readonly logger: Logger,
public readonly console: OutputStream,
public readonly filenameEnumerator: FilenameEnumerator,
Expand All @@ -31,7 +33,7 @@ class Context {
delete options.password;
const console = options.silent ? new NullOutputStream() : new ConsoleOutputStream();
const files = Context.getFileNames(options.config);
await Logger.purge(files.log, 9);
await Logger.purge(files.log, Context.MAX_LOG_FILES);
const logger = Context.getLogger(files.log, false);
logger.separator();
try {
Expand Down
32 changes: 18 additions & 14 deletions src/data/directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Subdirectory extends RootDirectory {
//------------------------------------------------------------------------------------------------------------------

public constructor(parent: Directory, public readonly name: string) {
super(node.path.join(parent.absolutePath, name))
super(node.path.join(parent.absolutePath, name));
this.relativePath = parent instanceof Subdirectory ? node.path.join(parent.relativePath, name) : name;
}
}
Expand All @@ -41,25 +41,25 @@ type Directory = RootDirectory | Subdirectory;

abstract class MappedDirectoryBase<T extends RootDirectory> {

private readonly _files = readonly({
private readonly _files = {
bySourceName: new Map<string, MappedFile>(),
byDestinationName: new Map<string, MappedFile>()
});
} as const;

public readonly files = readonly({
public readonly files = {
bySourceName: new ImmutableMap(this._files.bySourceName),
byDestinationName: new ImmutableMap(this._files.byDestinationName)
});
} as const;

private readonly _subdirectories = readonly({
private readonly _subdirectories = {
bySourceName: new Map<string, MappedSubdirectory>(),
byDestinationName: new Map<string, MappedSubdirectory>()
});
} as const;

public readonly subdirectories = readonly({
public readonly subdirectories = {
bySourceName: new ImmutableMap(this._subdirectories.bySourceName),
byDestinationName: new ImmutableMap(this._subdirectories.byDestinationName)
});
} as const;

//------------------------------------------------------------------------------------------------------------------
// Initialization
Expand Down Expand Up @@ -124,7 +124,9 @@ abstract class MappedDirectoryBase<T extends RootDirectory> {
this.deleteFrom(this._files.byDestinationName, fileOrSubdirectory.destination.name, fileOrSubdirectory);
} else {
this.deleteFrom(this._subdirectories.bySourceName, fileOrSubdirectory.source.name, fileOrSubdirectory);
this.deleteFrom(this._subdirectories.byDestinationName, fileOrSubdirectory.destination.name, fileOrSubdirectory);
this.deleteFrom(
this._subdirectories.byDestinationName, fileOrSubdirectory.destination.name, fileOrSubdirectory
);
}
}

Expand All @@ -137,7 +139,7 @@ abstract class MappedDirectoryBase<T extends RootDirectory> {
if (undefined === mapValue) {
throw new Error(`Internal error: Directory entry ${key} does not exist`);
} else if (mapValue !== value) {
throw new Error(`Internal error: ${key} points to the wrong directory entry`)
throw new Error(`Internal error: ${key} points to the wrong directory entry`);
} else {
map.delete(key);
}
Expand All @@ -147,11 +149,11 @@ abstract class MappedDirectoryBase<T extends RootDirectory> {
// Recursively count the the children
//------------------------------------------------------------------------------------------------------------------

public countChildren(statistics?: { files: number, subdirectories: number }) {
public countChildren(statistics?: { files: number, subdirectories: number; }) {
const realStatistics = statistics ?? { files: 0, subdirectories: 0 };
realStatistics.files += this._files.byDestinationName.size;
realStatistics.subdirectories += this._subdirectories.byDestinationName.size;
this._subdirectories.byDestinationName.forEach((subdirectory) => subdirectory.countChildren(realStatistics));
this._subdirectories.byDestinationName.forEach(subdirectory => subdirectory.countChildren(realStatistics));
return realStatistics;
}

Expand All @@ -168,6 +170,8 @@ abstract class MappedDirectoryBase<T extends RootDirectory> {

class MappedRootDirectory extends MappedDirectoryBase<RootDirectory> {

private static readonly MILLISECONDS_PER_SECOND = 1_000;

private lastSavedAtMs?: number;
private _hasUnsavedChanges = true;

Expand All @@ -194,7 +198,7 @@ class MappedRootDirectory extends MappedDirectoryBase<RootDirectory> {
public wasSavedWithinTheLastSeconds(seconds: number) {
return undefined === this.lastSavedAtMs
? false
: new Date().getTime() - this.lastSavedAtMs <= seconds * 1000;
: new Date().getTime() - this.lastSavedAtMs <= seconds * MappedRootDirectory.MILLISECONDS_PER_SECOND;
}

//------------------------------------------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/data/exception.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class FriendlyException extends Error {
// Initialization
//------------------------------------------------------------------------------------------------------------------

public constructor(message: string, public readonly exitCode: number = 1) {
public constructor(message: string, public readonly exitCode = 1) {
super(message);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/data/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class SuccessAndFailureStats {
//------------------------------------------------------------------------------------------------------------------

public get total() {
return this.success + this.failed
return this.success + this.failed;
}
}

Expand All @@ -50,7 +50,7 @@ class FileAndDirectoryStats {
//------------------------------------------------------------------------------------------------------------------

public get total() {
return this.files.total + this.directories.total
return this.files.total + this.directories.total;
}

//------------------------------------------------------------------------------------------------------------------
Expand All @@ -72,10 +72,10 @@ class SyncStats {
public readonly deleted = new FileAndDirectoryStats();
public readonly orphans = new FileAndDirectoryStats();
public readonly purged = new FileAndDirectoryStats();
public readonly unprocessable = readonly({
public readonly unprocessable = {
source: { symlinks: 0, other: 0 },
destination: { symlinks: 0, other: 0 }
});
};

public readonly index = {
hasLingeringOrphans: false,
Expand Down
62 changes: 36 additions & 26 deletions src/features/command-line-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class CommandLineParser {
silent: "silent",
help: "help",
version: "version"
}
};

//------------------------------------------------------------------------------------------------------------------
// Default options per command
Expand All @@ -50,13 +50,14 @@ class CommandLineParser {
command: "reconfigure",
...this.SHARED_DEFAULT_OPTIONS
})
}
};

//------------------------------------------------------------------------------------------------------------------
// Usage information
//------------------------------------------------------------------------------------------------------------------

private static showUsageAndExit(): never {
const configFile = this.DEFAULT_CONFIG_FILE;
this.exitWithMessage(`
Create an encrypted copy of a directory using 7-Zip.
|
Expand All @@ -71,7 +72,7 @@ class CommandLineParser {
| Options:
|
| --${this.OPTIONS.sevenZip}=<7_ZIP_EXECUTABLE> the 7-Zip executable to use
| --${this.OPTIONS.config}=<CONFIG_FILE> use the given configuration file (default: ${this.DEFAULT_CONFIG_FILE})
| --${this.OPTIONS.config}=<CONFIG_FILE> use the given configuration file (default: ${configFile})
| --${this.OPTIONS.dryRun} perform a trial run without making any changes
| --${this.OPTIONS.help} display this help and exit
| --${this.OPTIONS.password}=<PASSWORD> use this password instead of prompting for it
Expand Down Expand Up @@ -102,15 +103,15 @@ class CommandLineParser {

public static parse(argv: string[]) {
if (argv.filter(parameter => parameter.match(/^--?(v|version)$/)).length) {
this.showVersionAndExit();
return this.showVersionAndExit();
} else if (argv.filter(parameter => parameter.match(/^--?(h|help)$/)).length) {
this.showUsageAndExit();
return this.showUsageAndExit();
}
const { commands, options } = this.splitParameters(argv);
if (0 === commands.length) {
this.exitWithError('Missing command');
return this.exitWithError("Missing command");
} else if (1 < commands.length) {
this.exitWithError(`More than one command specified: ${commands.join(", ")}`);
return this.exitWithError(`More than one command specified: ${commands.join(", ")}`);
} else {
return this.assembleOptions(commands[0], options);
}
Expand All @@ -123,12 +124,17 @@ class CommandLineParser {
private static splitParameters(argv: string[]) {
const options = new Map<string, string | true>();
const commands = new Array<string>();
const prefix = "--";
const separator = "=";
const minLength = prefix.length + separator.length;
argv.forEach(argument => {
if (argument.startsWith("--")) {
const index = argument.indexOf("=");
const key = 3 <= index ? argument.substring(2, index).trim() : argument.substring(2).trim();
const value = 3 <= index ? argument.substring(index + 1) : true;
options.set(key, "string" === typeof value && this.OPTIONS.password !== key ? value.trim() : value)
if (argument.startsWith(prefix)) {
const index = argument.indexOf(separator);
const key = minLength <= index
? argument.substring(prefix.length, index).trim()
: argument.substring(prefix.length).trim();
const value = minLength <= index ? argument.substring(index + separator.length) : true;
options.set(key, "string" === typeof value && this.OPTIONS.password !== key ? value.trim() : value);
} else {
this.getInternalKey(this.DEFAULT_OPTIONS, argument, false);
commands.push(argument);
Expand All @@ -154,25 +160,25 @@ class CommandLineParser {
//------------------------------------------------------------------------------------------------------------------

private static getDefaultOptions(command: string) {
const defaultOptionsMap: { [index: string]: TaskOptions } = this.DEFAULT_OPTIONS;
const defaultOptionsMap: { [index: string]: TaskOptions; } = this.DEFAULT_OPTIONS;
const defaultOptions = defaultOptionsMap[this.getInternalKey(this.DEFAULT_OPTIONS, command, false)];
if (defaultOptions) {
return defaultOptions;
} else {
this.exitWithError(`Internal error - no default options for command "${command}"`);
}
return defaultOptions
? defaultOptions
: this.exitWithError(`Internal error - no default options for command "${command}"`);
}

//------------------------------------------------------------------------------------------------------------------
// Set a single option value
//------------------------------------------------------------------------------------------------------------------

private static setOption(command: string, defaultOptions: object, suppliedKey: string, suppliedValue: boolean | string) {
private static setOption(
command: string, defaultOptions: object, suppliedKey: string, suppliedValue: boolean | string
) {
const defaultKey = this.getInternalKey(this.OPTIONS, suppliedKey, true);
if ("command" === suppliedKey || !(defaultKey in defaultOptions)) {
this.exitWithError(`Command "${command}" does not support option --${suppliedKey}`);
}
const defaultValue = (defaultOptions as any)[defaultKey];
const defaultValue = asAny(defaultOptions)[defaultKey];
if ("boolean" === typeof defaultValue) {
if ("boolean" !== typeof suppliedValue) {
this.exitWithError(`Option --${suppliedKey} can't have a value assigned`);
Expand All @@ -182,16 +188,20 @@ class CommandLineParser {
this.exitWithError(`Option --${suppliedKey} requires a value`);
}
}
(defaultOptions as any)[defaultKey] = suppliedValue;
asAny(defaultOptions)[defaultKey] = suppliedValue;
}

//------------------------------------------------------------------------------------------------------------------
// Get the internal key for a command or option (e.g. 7-zip => sevenZip)
//------------------------------------------------------------------------------------------------------------------

private static getInternalKey(mapping: { [index: string]: string }, suppliedKey: string, isOption: true): string
private static getInternalKey(mapping: { [index: string]: { command: string } }, suppliedKey: string, isOption: false): string
private static getInternalKey(mapping: { [index: string]: string | { command: string } }, suppliedKey: string, isOption: boolean) {
private static getInternalKey(mapping: { [index: string]: string; }, suppliedKey: string, isOption: true): string;
private static getInternalKey(
mapping: { [index: string]: { command: string; }; }, suppliedKey: string, isOption: false
): string;
private static getInternalKey(
mapping: { [index: string]: string | { command: string; }; }, suppliedKey: string, isOption: boolean
) {
for (const internalKey of Object.keys(mapping)) {
const mappedValue = mapping[internalKey];
const externalKey = "string" === typeof mappedValue ? mappedValue : mappedValue.command;
Expand All @@ -200,9 +210,9 @@ class CommandLineParser {
}
}
if (isOption) {
this.exitWithError(`Invalid option --${suppliedKey}`);
return this.exitWithError(`Invalid option --${suppliedKey}`);
} else {
this.exitWithError(`Invalid argument "${suppliedKey}"`);
return this.exitWithError(`Invalid argument "${suppliedKey}"`);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/features/config-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ConfigValidator {
} else if (FileUtils.exists(config)) {
return `${config} is not a regular file`;
} else if (FileUtils.existsAndIsDirectory(directory)) {
return true
return true;
} else if (FileUtils.exists(directory)) {
return `Directory ${directory} is not a directory`;
} else {
Expand Down Expand Up @@ -72,7 +72,7 @@ class ConfigValidator {
} else if (FileUtils.equals(resolvedSource, resolvedDestination)) {
return "The destination directory can't be the same as the source directory";
} else if (FileUtils.isParentChild(resolvedDestination, config)) {
return "The destination directory must not contain the configuration file"
return "The destination directory must not contain the configuration file";
} else if (FileUtils.isParentChild(resolvedDestination, resolvedSource)) {
return "The source directory must not be inside the destination directory";
} else if (FileUtils.isParentChild(resolvedSource, resolvedDestination)) {
Expand Down
2 changes: 1 addition & 1 deletion src/features/database-assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class DatabaseAssembler {
this.assembleFilesAndSubdirectories(database, json);
return database;
} catch (exception) {
rethrow(exception, message => `Failed to assemble database - ${message}`);
return rethrow(exception, message => `Failed to assemble database - ${message}`);
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/features/database-serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DatabaseSerializer {

private static serialize(database: MappedRootDirectory) {
const json: JsonDatabase = {
directories: database.subdirectories.bySourceName.sorted().map(directory => this.directoryToJson(directory)),
directories: database.subdirectories.bySourceName.sorted().map(dir => this.directoryToJson(dir)),
files: database.files.bySourceName.sorted().map(file => this.fileToJson(file)),
last: database.last
};
Expand All @@ -37,10 +37,10 @@ class DatabaseSerializer {
return {
source: directory.source.name,
destination: directory.destination.name,
directories: directory.subdirectories.bySourceName.sorted().map(subDirectory => this.directoryToJson(subDirectory)),
directories: directory.subdirectories.bySourceName.sorted().map(subDir => this.directoryToJson(subDir)),
files: directory.files.bySourceName.sorted().map(file => this.fileToJson(file)),
last: directory.last
}
};
}

//------------------------------------------------------------------------------------------------------------------
Expand All @@ -54,6 +54,6 @@ class DatabaseSerializer {
created: file.created,
modified: file.modified,
size: file.size
}
};
}
}
2 changes: 1 addition & 1 deletion src/features/file-listing-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class FileListingCreator {
//------------------------------------------------------------------------------------------------------------------

public static create(database: MappedRootDirectory) {
return this.recurseInto(database, []).join("\n") + "\n"
return this.recurseInto(database, []).join("\n") + "\n";
}

//------------------------------------------------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit c32933f

Please sign in to comment.