import { FileSystemSource, ListDirOptions, VirtualDirectory, VirtualFile } from "./types"; import { NginxFS } from "./NginxFS"; import { FileStashFs } from "./FileStashFs"; import { CachePolicy, NoCacheEntryError } from "./FsCache"; type Config = T extends FileSystemSource ? ReturnType : never; const CACHE = Symbol('Cache'); export interface Fs { get root(): VirtualDirectory[]; download(file: VirtualFile): Promise; listDir(dir: VirtualDirectory, options?: ListDirOptions): Promise<(VirtualDirectory|VirtualFile)[]>; } export class MountedFs implements Fs { private filesystems: Map> = new Map(); public readonly name: string = 'root'; constructor(filesystems: (Config | Config)[]) { for (const fsOptions of filesystems) { if (fsOptions.type === 'nginx') { this.filesystems.set(fsOptions.name, new NginxFS(fsOptions)); } else if (fsOptions.type === 'filestash') { this.filesystems.set(fsOptions.name, new FileStashFs(fsOptions)); } } } get root() { return [...this.filesystems.values()].map(fs => fs.root); } download(file: VirtualFile) { const fs = this.filesystems.get(file.source); if (!fs) { throw new Error(`Filesystem ${file.source} not found`); } return fs.download(file); } async listDir(dir: VirtualDirectory, options?: ListDirOptions) { const fs = this.filesystems.get(dir.source); if (!fs) { throw new Error(`Filesystem ${dir.source} not found`); } const result = await fs.listDir(dir, options); return result; } async *search(query: string, cancelToken?: AbortSignal) { query = query.toLocaleLowerCase(); for (const fs of this.filesystems.values()) { const dirQueue = [fs.root]; while (dirQueue.length > 0) { const dir = dirQueue.shift()!; try { const children = await this.listDir(dir, {cache: CachePolicy.onlyCache}); for (const child of children) { if (cancelToken?.aborted) { return; } if (child.type === 'directory') { dirQueue.push(child); } else if (child.name.toLocaleLowerCase().includes(query)) { yield child; } } } catch (err) { if (!(err instanceof NoCacheEntryError)) { console.warn(`Failed to list directory ${dir.path.join('/')}`, err); } continue; } } } } async *buildIndex() { let counter = 0; for (const fs of this.filesystems.values()) { const dirQueue = [fs.root]; while (dirQueue.length > 0) { const dir = dirQueue.shift()!; for (const child of await this.listDir(dir)) { if (child.type === 'directory') { dirQueue.push(child); } counter++; } yield counter } } } } // export class SearchableFilesystem implements Fs { // public allFiles: VirtualFile[] = []; // constructor(private fs: MountedFs) {} // async precacheDir(dir: VirtualDirectory) { // for (const child of await this.fs.listDir(dir, {cache: CachePolicy.useCache})) { // if (child.type === 'file') { // this.allFiles.push(child); // } else { // await this.precacheDir(child as VirtualDirectory); // } // } // } // async search(query: string) { // await Promise.all(this.fs.root.map((root) => this.precacheDir(root))); // } // get root() { // return this.fs.root; // } // async listDir(dir: VirtualDirectory) { // return this.fs.listDir(dir); // } // async download(file: VirtualFile) { // return this.fs.download(file); // } // }