mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 15:43:49 +01:00
84 lines
3.8 KiB
TypeScript
84 lines
3.8 KiB
TypeScript
import {
|
|
Directory,
|
|
isAbsolutePath,
|
|
BasicDirectory,
|
|
resolveValidatedDirectory,
|
|
oneValidCharacter,
|
|
directoryRegexString,
|
|
AbsolutePath,
|
|
} from "./Directory";
|
|
/** Filepath Rules:
|
|
* 1. File extension cannot contain a "/"
|
|
* 2. Last character before the extension cannot be a "/" as this would be a blank filename
|
|
* 3. Must not contain a leading "/"
|
|
* 4. Directory names cannot be 0-length (no "//")
|
|
* 5. The characters *, ?, [, and ] cannot exist in the filepath*/
|
|
type BasicFilePath = string & { __type: "FilePath" };
|
|
|
|
/** A file path that is also an absolute path. Additional absolute rules:
|
|
* 1. Specific directory names "." and ".." are disallowed
|
|
* Absoluteness is typechecked with isAbsolutePath in DirectoryPath.ts */
|
|
export type FilePath = BasicFilePath & AbsolutePath;
|
|
|
|
// Capturing group named file which captures the entire filename part of a file path.
|
|
const filenameRegexString = `(?<file>${oneValidCharacter}+\\.${oneValidCharacter}+)$`;
|
|
|
|
/** Regex made of the two above regex parts to test for a whole valid filepath. */
|
|
const basicFilePathRegex = new RegExp(directoryRegexString + filenameRegexString) as RegExp & {
|
|
exec: (path: string) => null | { groups: { directory: BasicDirectory; file: FilePath } };
|
|
};
|
|
|
|
/** Simple validation function with no modification. Can be combined with isAbsolutePath to get a real FilePath */
|
|
export function isFilePath(path: string): path is BasicFilePath {
|
|
return basicFilePathRegex.test(path);
|
|
}
|
|
|
|
export function asFilePath<T extends string>(input: T): T & FilePath {
|
|
if (isFilePath(input) && isAbsolutePath(input)) return input;
|
|
throw new Error(`${input} failed to validate as a FilePath.`);
|
|
}
|
|
|
|
export function getFilenameOnly<T extends BasicFilePath>(path: T): T & FilePath {
|
|
const start = path.lastIndexOf("/") + 1;
|
|
return path.substring(start) as T & FilePath;
|
|
}
|
|
|
|
/** Validate while also capturing and returning directory and file parts */
|
|
function getFileParts(path: string): { directory: BasicDirectory; file: FilePath } | null {
|
|
const result = basicFilePathRegex.exec(path) as null | { groups: { directory: BasicDirectory; file: FilePath } };
|
|
return result ? result.groups : null;
|
|
}
|
|
|
|
/** Sanitizes a player input and resolves a relative file path to an absolute one.
|
|
* @param path The player-provided path string. Can include relative directories.
|
|
* @param base The absolute base for resolving a relative path. */
|
|
export function resolveFilePath(path: string, base = "" as FilePath | Directory): FilePath | null {
|
|
if (isAbsolutePath(path)) {
|
|
if (path.startsWith("/")) path = path.substring(1);
|
|
// Because we modified the string since checking absoluteness, we have to assert that it's still absolute here.
|
|
return isFilePath(path) ? (path as FilePath) : null;
|
|
}
|
|
// Turn base into a DirectoryName in case it was not
|
|
base = getBaseDirectory(base);
|
|
const pathParts = getFileParts(path);
|
|
if (!pathParts) return null;
|
|
const directory = resolveValidatedDirectory(pathParts.directory, base);
|
|
// Have to specifically check null here instead of truthiness, because empty string is a valid DirectoryPath
|
|
return directory === null ? null : combinePath(directory, pathParts.file);
|
|
}
|
|
|
|
/** Remove the file part from an absolute path (FilePath or DirectoryPath - no modification is done for a DirectoryPath) */
|
|
function getBaseDirectory(path: FilePath | Directory): Directory {
|
|
return path.replace(/[^/]+\.[^/]+$/, "") as Directory;
|
|
}
|
|
/** Combine an absolute DirectoryPath and FilePath to create a new FilePath */
|
|
export function combinePath<T extends FilePath>(directory: Directory, file: T): T {
|
|
// Preserves the specific file type because the filepart is preserved.
|
|
return (directory + file) as T;
|
|
}
|
|
|
|
export function removeDirectoryFromPath(directory: Directory, path: FilePath): FilePath | null {
|
|
if (!path.startsWith(directory)) return null;
|
|
return path.substring(directory.length) as FilePath;
|
|
}
|