@difizen/mana-app
Version:
991 lines (867 loc) • 29.7 kB
text/typescript
/* eslint-disable @typescript-eslint/no-use-before-define */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// based on https://github.com/microsoft/vscode/blob/04c36be045a94fee58e5f8992d3e3fd980294a84/src/vs/platform/files/common/files.ts
import type { Disposable as IDisposable, Event } from '@difizen/mana-common';
import { URI } from '@difizen/mana-common';
export enum FileOperation {
CREATE,
DELETE,
MOVE,
COPY,
}
export class FileOperationEvent {
public readonly resource: URI;
public readonly operation: FileOperation;
public readonly target?: FileStatWithMetadata | undefined;
constructor(resource: URI, operation: FileOperation.DELETE);
constructor(
resource: URI,
operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY,
target: FileStatWithMetadata,
);
constructor(resource: URI, operation: FileOperation, target?: FileStatWithMetadata) {
this.resource = resource;
this.operation = operation;
this.target = target;
}
isOperation(operation: FileOperation.DELETE): boolean;
isOperation(
operation: FileOperation.MOVE | FileOperation.COPY | FileOperation.CREATE,
): this is { readonly target: FileStatWithMetadata };
isOperation(operation: FileOperation): boolean {
return this.operation === operation;
}
}
/**
* Possible changes that can occur to a file.
*/
export enum FileChangeType {
UPDATED = 0,
ADDED = 1,
DELETED = 2,
}
/**
* Identifies a single change in a file.
*/
export interface FileChange {
/**
* The type of change that occurred to the file.
*/
readonly type: FileChangeType;
/**
* The unified resource identifier of the file that changed.
*/
readonly resource: URI;
}
export class FileChangesEvent {
public readonly changes: readonly FileChange[];
constructor(changes: readonly FileChange[]) {
this.changes = changes;
}
/**
* Returns true if this change event contains the provided file with the given change type (if provided). In case of
* type DELETED, this method will also return true if a folder got deleted that is the parent of the
* provided file path.
*/
contains(resource: URI, type?: FileChangeType): boolean {
if (!resource) {
return false;
}
const checkForChangeType = typeof type === 'number';
return this.changes.some((change) => {
if (checkForChangeType && change.type !== type) {
return false;
}
// For deleted also return true when deleted folder is parent of target path
if (change.type === FileChangeType.DELETED) {
return resource.includes(change.resource);
}
return resource.toString() === change.resource.toString();
});
}
/**
* Returns the changes that describe added files.
*/
getAdded(): FileChange[] {
return this.getOfType(FileChangeType.ADDED);
}
/**
* Returns if this event contains added files.
*/
gotAdded(): boolean {
return this.hasType(FileChangeType.ADDED);
}
/**
* Returns the changes that describe deleted files.
*/
getDeleted(): FileChange[] {
return this.getOfType(FileChangeType.DELETED);
}
/**
* Returns if this event contains deleted files.
*/
gotDeleted(): boolean {
return this.hasType(FileChangeType.DELETED);
}
/**
* Returns the changes that describe updated files.
*/
getUpdated(): FileChange[] {
return this.getOfType(FileChangeType.UPDATED);
}
/**
* Returns if this event contains updated files.
*/
gotUpdated(): boolean {
return this.hasType(FileChangeType.UPDATED);
}
private getOfType(type: FileChangeType): FileChange[] {
return this.changes.filter((change) => change.type === type);
}
private hasType(type: FileChangeType): boolean {
return this.changes.some((change) => change.type === type);
}
}
export interface BaseStat {
/**
* The unified resource identifier of this file or folder.
*/
resource: URI;
/**
* The name which is the last segment
* of the {{path}}.
*/
name: string;
/**
* The size of the file.
*
* The value may or may not be resolved as
* it is optional.
*/
size?: number | undefined;
/**
* The last modification date represented as millis from unix epoch.
*
* The value may or may not be resolved as
* it is optional.
*/
mtime?: number | undefined;
/**
* The creation date represented as millis from unix epoch.
*
* The value may or may not be resolved as
* it is optional.
*/
ctime?: number | undefined;
/**
* A unique identifier that represents the
* current state of the file or directory.
*
* The value may or may not be resolved as
* it is optional.
*/
etag?: string | undefined;
}
export namespace BaseStat {
export function is(arg: Record<any, any> | undefined): arg is BaseStat {
return (
!!arg &&
typeof arg === 'object' &&
// eslint-disable-next-line @typescript-eslint/no-explicit-any
'resource' in arg &&
(arg as any).resource instanceof URI &&
'name' in arg &&
typeof (arg as any).name === 'string'
);
}
}
export interface BaseStatWithMetadata extends BaseStat {
mtime: number;
ctime: number;
etag: string;
size: number;
}
/**
* A file resource with meta information.
*/
export interface FileStat extends BaseStat {
/**
* The resource is a file.
*/
isFile: boolean;
/**
* The resource is a directory.
*/
isDirectory: boolean;
/**
* The resource is a symbolic link.
*/
isSymbolicLink: boolean;
/**
* The children of the file stat or undefined if none.
*/
children?: FileStat[];
}
export namespace FileStat {
export function is(arg: Record<any, any> | undefined): arg is FileStat {
if (BaseStat.is(arg) && 'isFile' in arg) {
return (
'isFile' in arg &&
typeof (arg as any).isFile === 'boolean' &&
'isDirectory' in arg &&
typeof (arg as any).isDirectory === 'boolean' &&
'isSymbolicLink' in arg &&
typeof (arg as any).isSymbolicLink === 'boolean'
);
}
return false;
}
export function asFileType(stat: FileStat): FileType {
let res = 0;
if (stat.isFile) {
res += FileType.File;
} else if (stat.isDirectory) {
res += FileType.Directory;
}
if (stat.isSymbolicLink) {
res += FileType.SymbolicLink;
}
return res;
}
export function toStat(stat: FileStat): Stat | ({ type: FileType } & Partial<Stat>) {
return {
type: asFileType(stat),
ctime: stat.ctime!,
mtime: stat.mtime!,
size: stat.size!,
};
}
export function fromStat(resource: URI, stat: Stat): FileStatWithMetadata;
export function fromStat(
resource: URI,
stat: { type: FileType } & Partial<Stat>,
): FileStat;
export function fromStat(
resource: URI,
stat: Stat | ({ type: FileType } & Partial<Stat>),
): FileStat {
return {
resource,
name: resource.path.base || resource.path.toString(),
isFile: (stat.type & FileType.File) !== 0,
isDirectory: (stat.type & FileType.Directory) !== 0,
isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0,
mtime: stat.mtime,
ctime: stat.ctime,
size: stat.size,
etag: etag({ mtime: stat.mtime, size: stat.size }),
};
}
export function dir(
resource: string | URI,
stat?: Partial<Omit<Stat, 'type'>>,
): FileStat {
return fromStat(resource instanceof URI ? resource : new URI(resource), {
type: FileType.Directory,
...stat,
});
}
export function file(
resource: string | URI,
stat?: Partial<Omit<Stat, 'type'>>,
): FileStat {
return fromStat(resource instanceof URI ? resource : new URI(resource), {
type: FileType.File,
...stat,
});
}
}
export interface FileStatWithMetadata extends FileStat, BaseStatWithMetadata {
mtime: number;
ctime: number;
etag: string;
size: number;
children?: FileStatWithMetadata[];
}
export interface ResolveFileResult {
stat?: FileStat;
success: boolean;
}
export interface ResolveFileResultWithMetadata extends ResolveFileResult {
stat?: FileStatWithMetadata;
}
export interface WriteFileOptions {
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
readonly mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
readonly etag?: string;
}
export interface ReadFileOptions extends FileReadStreamOptions {
/**
* The optional etag parameter allows to return early from resolving the resource if
* the contents on disk match the etag. This prevents accumulated reading of resources
* that have been read already with the same etag.
* It is the task of the caller to makes sure to handle this error case from the promise.
*/
readonly etag?: string;
}
export interface WriteFileOptions {
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
readonly mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
readonly etag?: string;
}
export interface ResolveFileOptions {
/**
* Automatically continue resolving children of a directory until the provided resources
* are found.
*/
readonly resolveTo?: readonly URI[];
/**
* Automatically continue resolving children of a directory if the number of children is 1.
*/
readonly resolveSingleChildDescendants?: boolean;
/**
* Will resolve mtime, ctime, size and etag of files if enabled. This can have a negative impact
* on performance and thus should only be used when these values are required.
*/
readonly resolveMetadata?: boolean;
}
export interface ResolveMetadataFileOptions extends ResolveFileOptions {
readonly resolveMetadata: true;
}
export interface FileOperationOptions {
/**
* Indicates that a user action triggered the opening, e.g.
* via mouse or keyboard use. Default is true.
*/
fromUserGesture?: boolean;
}
export interface MoveFileOptions
extends FileOperationOptions,
Partial<FileOverwriteOptions> {}
export interface CopyFileOptions
extends FileOperationOptions,
Partial<FileOverwriteOptions> {}
export interface CreateFileOptions
extends FileOperationOptions,
Partial<FileOverwriteOptions> {}
export class FileOperationError extends Error {
public fileOperationResult: FileOperationResult;
public options?: (ReadFileOptions & WriteFileOptions & CreateFileOptions) | undefined;
constructor(
message: string,
fileOperationResult: FileOperationResult,
options?: ReadFileOptions & WriteFileOptions & CreateFileOptions,
) {
super(message);
this.fileOperationResult = fileOperationResult;
this.options = options;
Object.setPrototypeOf(this, FileOperationError.prototype);
}
}
export enum FileOperationResult {
FILE_IS_DIRECTORY,
FILE_NOT_FOUND,
FILE_NOT_MODIFIED_SINCE,
FILE_MODIFIED_SINCE,
FILE_MOVE_CONFLICT,
FILE_READ_ONLY,
FILE_PERMISSION_DENIED,
FILE_TOO_LARGE,
FILE_INVALID_PATH,
FILE_EXCEEDS_MEMORY_LIMIT,
FILE_NOT_DIRECTORY,
FILE_OTHER_ERROR,
}
export interface FileOverwriteOptions {
/**
* Overwrite the file to create if it already exists on disk. Otherwise
* an error will be thrown (FILE_MODIFIED_SINCE).
*/
overwrite: boolean;
}
export interface FileReadStreamOptions {
/**
* Is an integer specifying where to begin reading from in the file. If position is undefined,
* data will be read from the current file position.
*/
readonly position?: number;
/**
* Is an integer specifying how many bytes to read from the file. By default, all bytes
* will be read.
*/
readonly length?: number;
/**
* If provided, the size of the file will be checked against the limits.
*/
limits?: {
readonly size?: number;
readonly memory?: number;
};
}
export interface FileUpdateOptions {
readEncoding: string;
writeEncoding: string;
overwriteEncoding: boolean;
}
export interface FileUpdateResult extends Stat {
encoding: string;
}
export interface FileWriteOptions {
overwrite: boolean;
create: boolean;
}
export interface FileOpenOptions {
create: boolean;
}
export interface FileDeleteOptions {
recursive: boolean;
useTrash: boolean;
}
export enum FileType {
Unknown = 0,
File = 1,
Directory = 2,
SymbolicLink = 64,
}
export interface Stat {
type: FileType;
/**
* The last modification date represented as millis from unix epoch.
*/
mtime: number;
/**
* The creation date represented as millis from unix epoch.
*/
ctime: number;
size: number;
}
export interface WatchOptions {
recursive: boolean;
excludes: string[];
}
export enum FileSystemProviderCapabilities {
FileReadWrite = 1 << 1,
FileOpenReadWriteClose = 1 << 2,
FileReadStream = 1 << 4,
FileFolderCopy = 1 << 3,
PathCaseSensitive = 1 << 10,
Readonly = 1 << 11,
Trash = 1 << 12,
Access = 1 << 24,
Update = 1 << 25,
}
export enum FileSystemProviderErrorCode {
FileExists = 'EntryExists',
FileNotFound = 'EntryNotFound',
FileNotADirectory = 'EntryNotADirectory',
FileIsADirectory = 'EntryIsADirectory',
FileExceedsMemoryLimit = 'EntryExceedsMemoryLimit',
FileTooLarge = 'EntryTooLarge',
NoPermissions = 'NoPermissions',
Unavailable = 'Unavailable',
Unknown = 'Unknown',
}
export class FileSystemProviderError extends Error {
public readonly code: FileSystemProviderErrorCode;
constructor(message: string, code: FileSystemProviderErrorCode) {
super(message);
this.code = code;
Object.setPrototypeOf(this, FileSystemProviderError.prototype);
}
}
export function createFileSystemProviderError(
error: Error | string,
code: FileSystemProviderErrorCode,
): FileSystemProviderError {
const providerError = new FileSystemProviderError(error.toString(), code);
markAsFileSystemProviderError(providerError, code);
return providerError;
}
export function ensureFileSystemProviderError(error?: Error): Error {
if (!error) {
return createFileSystemProviderError(
'Unknown Error',
FileSystemProviderErrorCode.Unknown,
); // https://github.com/Microsoft/vscode/issues/72798
}
return error;
}
export const FileSystemProvider = Symbol('FileSystemProvider');
/**
* A {@link FileSystemProvider} provides the capabilities to read, write, discover, and to manage files and folders
* of the underlying (potentially virtual) file system. {@link FileSystemProvider}s can be used to serve files from both the
* local disk as well as remote locations like ftp-servers, REST-services etc. A {@link FileSystemProvider} is registered for a certain
* scheme and can handle all resources whose uri does conform to that scheme.
*/
export interface FileSystemProvider {
/** The {@link FileSystemProviderCapabilities} for this provider. */
readonly capabilities: FileSystemProviderCapabilities;
/** * Event that is fired if the capabilities of this provider have changed. */
readonly onDidChangeCapabilities: Event<void>;
/** Event that is fired if a (watched) file in the filesystem of this provider has changed. */
readonly onDidChangeFile: Event<readonly FileChange[]>;
/** Event that is fired if an error occurred when watching files in the filesystem of this provider. */
readonly onFileWatchError: Event<void>;
/**
* Watch the given resource and react to changes by firing the {@link FileSystemProvider#onDidChangeFile} event.
* @param resource `URI` of the resource to be watched.
* @param opts Options to define if the resource should be watched recursively and to
* provide a set of resources that should be excluded from watching.
*
* @returns A `Disposable` that can be invoked to stop watching the resource.
*/
watch: (resource: URI, opts: WatchOptions) => IDisposable;
/**
* Retrieve metadata about a given file.
*
* @param uri The `URI` of the file to retrieve meta data about.
* @returns A promise of the metadata about the resource.
*/
stat: (resource: URI) => Promise<Stat>;
/**
* Create a new directory using the given resource uri.
* @param resource The `URI` of the new folder.
*/
mkdir: (resource: URI) => Promise<void>;
/**
* Retrieve the content of a given directory.
* @param resource The `URI` of the directory.
*
* @returns A map containing the {@link FileType} for each child resource, identified by name.
*/
readdir: (resource: URI) => Promise<[string, FileType][]>;
/**
* Delete the given resource.
* @param resource The `URI` of the resource to delete.
* @param opts Options to define if files should be deleted recursively and if the trash should be used.
*/
delete: (resource: URI, opts: FileDeleteOptions) => Promise<void>;
/**
* Rename a file or folder.
* @param from `URI` of the existing file or folder.
* @param to `URI` of the target location.
* @param opts Options to define if existing files should be overwritten.
*/
rename: (from: URI, to: URI, opts: FileOverwriteOptions) => Promise<void>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithFileFolderCopyCapability}.
* See {@link FileSystemProviderWithFileFolderCopyCapability#copy}} for additional documentation.
*/
copy?: (from: URI, to: URI, opts: FileOverwriteOptions) => Promise<void>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithFileReadWriteCapability}.
* See {@link FileSystemProviderWithFileReadWriteCapability#readFile} for additional documentation.
*/
readFile?: (resource: URI) => Promise<Uint8Array>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithFileReadWriteCapability}.
* See {@link FileSystemProviderWithFileReadWriteCapability#writeFile} for additional documentation.
*/
writeFile?: (
resource: URI,
content: Uint8Array,
opts: FileWriteOptions,
) => Promise<void>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}.
* See {@link FileSystemProviderWithOpenReadWriteCloseCapability#open} for additional documentation.
*/
open?: (resource: URI, opts: FileOpenOptions) => Promise<number>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}.
* See {@link FileSystemProviderWithOpenReadWriteCloseCapability#close} for additional documentation.
*/
close?: (fd: number) => Promise<void>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}.
* See {@link FileSystemProviderWithOpenReadWriteCloseCapability#read} for additional documentation.
*/
read?: (
fd: number,
pos: number,
data: Uint8Array,
offset: number,
length: number,
) => Promise<number>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}.
* See {@link FileSystemProviderWithOpenReadWriteCloseCapability#write} for additional documentation.
*/
write?: (
fd: number,
pos: number,
data: Uint8Array,
offset: number,
length: number,
) => Promise<number>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithAccessCapability}.
* See {@link FileSystemProviderWithAccessCapability#access} for additional documentation.
*/
access?: (resource: URI, mode?: number) => Promise<void>;
/**
* Optional function that has to be implemented by {@link FileSystemProviderWithAccessCapability}.
* See {@link FileSystemProviderWithAccessCapability#fsPath} for additional documentation.
*/
fsPath?: (resource: URI) => Promise<string>;
}
/**
* Subtype of {@link FileSystemProvider} that ensures that the optional functions needed for providers, that should be
* able access files, are implemented.
*/
export interface FileSystemProviderWithAccessCapability extends FileSystemProvider {
/**
* Test if the user has the permission to access the given file in the specified mode.
* @param resource The `URI` of the file that should be tested.
* @param mode The access mode that should be tested.
*
* @returns A promise that resolves if the user has the required permissions, should be rejected otherwise.
*/
access: (resource: URI, mode?: number) => Promise<void>;
/**
* Derive the platform specific file system path that is represented by the resource.
* @param resource `URI` of the resource to derive the path from.
*
* @returns A promise of the corresponding file system path.
*/
fsPath: (resource: URI) => Promise<string>;
}
export function hasAccessCapability(
provider: FileSystemProvider,
): provider is FileSystemProviderWithAccessCapability {
return !!(provider.capabilities & FileSystemProviderCapabilities.Access);
}
/**
* Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers
* that should be able to read & write files, are implemented.
*/
export interface FileSystemProviderWithFileReadWriteCapability
extends FileSystemProvider {
/**
* Read the contents of the given file as stream.
* @param resource The `URI` of the file.
*
* @return The readable stream of the given file.
*/
readFile: (resource: URI) => Promise<Uint8Array>;
/**
* Write data to a file, replacing its entire contents.
* @param resource The uri of the file.
* @param content The new content of the file.
* @param opts Options to define if the file should be created if missing and if an existing file should be overwritten.
*/
writeFile: (
resource: URI,
content: Uint8Array,
opts: FileWriteOptions,
) => Promise<void>;
}
export function hasReadWriteCapability(
provider: FileSystemProvider,
): provider is FileSystemProviderWithFileReadWriteCapability {
return !!(provider.capabilities & FileSystemProviderCapabilities.FileReadWrite);
}
/**
* Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers that should be able to copy
* file folders, are implemented.
*/
export interface FileSystemProviderWithFileFolderCopyCapability
extends FileSystemProvider {
/**
* Copy files or folders.
* @param from `URI` of the existing file or folder.
* @param to `URI` of the destination location.
* @param opts Options to define if existing files should be overwritten.
*/
copy: (from: URI, to: URI, opts: FileOverwriteOptions) => Promise<void>;
}
export function hasFileFolderCopyCapability(
provider: FileSystemProvider,
): provider is FileSystemProviderWithFileFolderCopyCapability {
return !!(provider.capabilities & FileSystemProviderCapabilities.FileFolderCopy);
}
/**
* Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers that should be able to open,read, write
* or close files, are implemented.
*/
export interface FileSystemProviderWithOpenReadWriteCloseCapability
extends FileSystemProvider {
/**
* Open the give file.
* @param resource The `URI` of the file to open.
* @param opts Options to define if the file should be created if it does not exist yet.
*
* @returns A promise of the file descriptor that resolves after the file is open.
*/
open: (resource: URI, opts: FileOpenOptions) => Promise<number>;
/**
* Close the file with the given file descriptor.
* @param fd the file descriptor to close.
*/
close: (fd: number) => Promise<void>;
/**
* Read specified content from a given file descriptor into a data buffer.
* @param fd The file descriptor referencing the file to read from.
* @param pos The offset from the beginning of the file from which data should be read.
* @param data The buffer that the data will be written to.
* @param offset The offset in the buffer at which to start writing.
* @param length The number of bytes to read.
*
* @returns A promise of the number of bytes read.
*/
read: (
fd: number,
pos: number,
data: Uint8Array,
offset: number,
length: number,
) => Promise<number>;
/**
* Write specified content from the data buffer to the file referenced by the given file descriptor.
* @param fd The file descriptor referencing the file to write to.
* @param pos The offset from the beginning of the file where this data should be written.
* @param offset The part of the buffer to be read from.
* @param length The number of bytes to write.
*
* @returns A promise of the number of bytes written.
*/
write: (
fd: number,
pos: number,
data: Uint8Array,
offset: number,
length: number,
) => Promise<number>;
}
export function hasOpenReadWriteCloseCapability(
provider: FileSystemProvider,
): provider is FileSystemProviderWithOpenReadWriteCloseCapability {
return !!(
provider.capabilities & FileSystemProviderCapabilities.FileOpenReadWriteClose
);
}
export function markAsFileSystemProviderError(
error: Error,
code: FileSystemProviderErrorCode,
): Error {
error.name = code ? `${code} (FileSystemError)` : 'FileSystemError';
return error;
}
export function toFileSystemProviderErrorCode(
error: Error | undefined | null,
): FileSystemProviderErrorCode {
// Guard against abuse
if (!error) {
return FileSystemProviderErrorCode.Unknown;
}
// FileSystemProviderError comes with the code
if (error instanceof FileSystemProviderError) {
return error.code;
}
// Any other error, check for name match by assuming that the error
// went through the markAsFileSystemProviderError() method
const match = /^(.+) \(FileSystemError\)$/.exec(error.name);
if (!match) {
return FileSystemProviderErrorCode.Unknown;
}
// eslint-disable-next-line default-case
switch (match[1]) {
case FileSystemProviderErrorCode.FileExists:
return FileSystemProviderErrorCode.FileExists;
case FileSystemProviderErrorCode.FileIsADirectory:
return FileSystemProviderErrorCode.FileIsADirectory;
case FileSystemProviderErrorCode.FileNotADirectory:
return FileSystemProviderErrorCode.FileNotADirectory;
case FileSystemProviderErrorCode.FileNotFound:
return FileSystemProviderErrorCode.FileNotFound;
case FileSystemProviderErrorCode.FileExceedsMemoryLimit:
return FileSystemProviderErrorCode.FileExceedsMemoryLimit;
case FileSystemProviderErrorCode.FileTooLarge:
return FileSystemProviderErrorCode.FileTooLarge;
case FileSystemProviderErrorCode.NoPermissions:
return FileSystemProviderErrorCode.NoPermissions;
case FileSystemProviderErrorCode.Unavailable:
return FileSystemProviderErrorCode.Unavailable;
}
return FileSystemProviderErrorCode.Unknown;
}
export function toFileOperationResult(error: Error): FileOperationResult {
// FileSystemProviderError comes with the result already
if (error instanceof FileOperationError) {
return error.fileOperationResult;
}
// Otherwise try to find from code
switch (toFileSystemProviderErrorCode(error)) {
case FileSystemProviderErrorCode.FileNotFound:
return FileOperationResult.FILE_NOT_FOUND;
case FileSystemProviderErrorCode.FileIsADirectory:
return FileOperationResult.FILE_IS_DIRECTORY;
case FileSystemProviderErrorCode.FileNotADirectory:
return FileOperationResult.FILE_NOT_DIRECTORY;
case FileSystemProviderErrorCode.NoPermissions:
return FileOperationResult.FILE_PERMISSION_DENIED;
case FileSystemProviderErrorCode.FileExists:
return FileOperationResult.FILE_MOVE_CONFLICT;
case FileSystemProviderErrorCode.FileExceedsMemoryLimit:
return FileOperationResult.FILE_EXCEEDS_MEMORY_LIMIT;
case FileSystemProviderErrorCode.FileTooLarge:
return FileOperationResult.FILE_TOO_LARGE;
default:
return FileOperationResult.FILE_OTHER_ERROR;
}
}
/**
* A hint to disable etag checking for reading/writing.
*/
export const ETAG_DISABLED = '';
export function etag(stat: { mtime: number; size: number }): string;
export function etag(stat: {
mtime: number | undefined;
size: number | undefined;
}): string | undefined;
export function etag(stat: {
mtime: number | undefined;
size: number | undefined;
}): string | undefined {
if (typeof stat.size !== 'number' || typeof stat.mtime !== 'number') {
return undefined;
}
return stat.mtime.toString(29) + stat.size.toString(31);
}
/**
* Helper to format a raw byte size into a human readable label.
*/
export class BinarySize {
static readonly KB = 1024;
static readonly MB = BinarySize.KB * BinarySize.KB;
static readonly GB = BinarySize.MB * BinarySize.KB;
static readonly TB = BinarySize.GB * BinarySize.KB;
static formatSize(size: number): string {
if (size < BinarySize.KB) {
return `${size}B`;
}
if (size < BinarySize.MB) {
return `${(size / BinarySize.KB).toFixed(2)}KB`;
}
if (size < BinarySize.GB) {
return `${(size / BinarySize.MB).toFixed(2)}MB`;
}
if (size < BinarySize.TB) {
return `${(size / BinarySize.GB).toFixed(2)}GB`;
}
return `${(size / BinarySize.TB).toFixed(2)}TB`;
}
}