import {UniversalPath} from '../path' import * as path from 'path' import * as os from 'os' import {uuid4} from '../data' import {ErrorWithContext} from '../../error/ErrorWithContext' import {Readable, Writable} from 'stream' import {Awaitable} from '../types' import {Collection} from '../../collection/Collection' /** * Error thrown when an operation is attempted on a non-existent file. */ export class FileNotFoundError extends ErrorWithContext { constructor(filename: string, context: any = {}) { super(`The specified file does not exist: ${filename}`, context) } } /** * Interface representing metadata that can be stored about a given file. */ export interface FileMetadata { /** * Tags associated with this file. */ tags?: string[], /** * The mime-type of this file. */ mimeType?: string, /** * Miscellaneous metadata about the file. */ misc?: {[key: string]: string}, } /** * Interface defining information about a file. */ export interface Stat { /** * UniversalPath resource pointing to the file in its filesystem. */ path: UniversalPath, /** * True if the file exists. False otherwise. */ exists: boolean, /** * The size, in bytes, of the file on the remote filesystem. * If `exists` is false, this number is undefined. */ sizeInBytes: number, /** * If specified, the mime-type of the remote file. */ mimeType?: string, /** * Tags associated with the remote file. */ tags: string[], /** * True if the resource exists as a directory. */ isDirectory: boolean, /** * True if the resource exists as a regular file. */ isFile: boolean, accessed?: Date, modified?: Date, created?: Date, } /** * Abstract base-class for remote filesystem implementations. */ export abstract class Filesystem { /** * Called when the Filesystem driver is initialized. Do any standup here. */ public open(): Awaitable {} // eslint-disable-line @typescript-eslint/no-empty-function /** * Called when the Filesystem driver is destroyed. Do any cleanup here. */ public close(): Awaitable {} // eslint-disable-line @typescript-eslint/no-empty-function /** * Get the URI prefix for this filesystem. * @example `file://` * @example `s3://` */ public abstract getPrefix(): string /** * Get a UniversalPath that refers to a file on this filesystem. * @param storePath */ public getPath(storePath: string): UniversalPath { return new UniversalPath(storePath, this) } /** * Store a file from the local filesystem into the remote filesystem. * * @example * ```ts * await store.putLocalFile({ * localPath: '/tmp/temp.file', * storePath: 'my/upload-key/temp.file', * mimeType: 'application/json', * tags: ['json', 'user-data'], * }) * ``` * * @param args */ public abstract putLocalFile(args: {localPath: string, storePath: string, mimeType?: string, tags?: string[], tag?: string}): Awaitable /** * Download a file in the remote filesystem to the local filesystem and return it as a UniversalPath. * @param args */ public abstract getStoreFileAsTemp(args: {storePath: string}): Awaitable /** * Open a readable stream for a file in the remote filesystem. * @param args */ public abstract getStoreFileAsStream(args: {storePath: string}): Awaitable /** * Open a writable stream for a file in the remote filesystem. * @param args */ public abstract putStoreFileAsStream(args: {storePath: string}): Awaitable /** * Fetch some information about a file that may or may not be in the remote filesystem without fetching the entire file. * @param args */ public abstract stat(args: {storePath: string}): Awaitable /** * If the file does not exist in the remote filesystem, create it. If it does exist, update the modify timestamps. * @param args */ public abstract touch(args: {storePath: string}): Awaitable /** * Remove the given resource(s) from the remote filesystem. * @param args */ public abstract remove(args: {storePath: string, recursive?: boolean }): Awaitable /** * Create the given path on the store as a directory, recursively. * @param args */ public abstract mkdir(args: {storePath: string}): Awaitable /** * Get the metadata object for the given file, if it exists. * @param storePath */ public abstract getMetadata(storePath: string): Awaitable /** * Set the metadata object for the given file, if the file exists. * @param storePath * @param meta */ public abstract setMetadata(storePath: string, meta: FileMetadata): Awaitable /** * List direct children of this resource. */ public abstract list(storePath: string): Awaitable> /** * Normalize the input tags into a single array of strings. This is useful for implementing the fluent * interface for `putLocalFile()`. * * @example * ```typescript * const tags: string[] = this._normalizeTags(args.tag, args.tags) * ``` * * @param tag * @param tags * @protected */ protected normalizeTags(tag?: string, tags?: string[]): string[] { if ( !tags ) { tags = [] } if ( tag ) { tags.push(tag) } return tags } /** * Generate the name of a temp-file on the LOCAL filesystem. * @protected */ protected tempName(): string { return path.resolve(os.tmpdir(), uuid4()) } }