diff --git a/src/apps/fuse/FuseApp.ts b/src/apps/fuse/FuseApp.ts index 9174ae3c9..4c8eaa75c 100644 --- a/src/apps/fuse/FuseApp.ts +++ b/src/apps/fuse/FuseApp.ts @@ -37,7 +37,10 @@ export class FuseApp { this.fuseContainer.offlineDriveContainer ); const open = new OpenCallback(this.fuseContainer.virtualDriveContainer); - const read = new ReadCallback(this.fuseContainer.virtualDriveContainer); + const read = new ReadCallback( + this.fuseContainer.virtualDriveContainer, + this.fuseContainer.offlineDriveContainer + ); const renameOrMove = new RenameOrMoveCallback( this.fuseContainer.virtualDriveContainer ); diff --git a/src/apps/fuse/callbacks/FuseErrors.ts b/src/apps/fuse/callbacks/FuseErrors.ts index 33bf146c8..5269fc841 100644 --- a/src/apps/fuse/callbacks/FuseErrors.ts +++ b/src/apps/fuse/callbacks/FuseErrors.ts @@ -11,8 +11,8 @@ export class FuseError extends Error { } export class FuseNoSuchFileOrDirectoryError extends FuseError { - constructor() { - super(FuseCodes.ENOENT, 'No such file or directory'); + constructor(readonly path: string) { + super(FuseCodes.ENOENT, `No such file or directory <${path}>`); } } diff --git a/src/apps/fuse/callbacks/GetAttributesCallback.ts b/src/apps/fuse/callbacks/GetAttributesCallback.ts index 6d4f6bf1e..47f440269 100644 --- a/src/apps/fuse/callbacks/GetAttributesCallback.ts +++ b/src/apps/fuse/callbacks/GetAttributesCallback.ts @@ -26,11 +26,11 @@ export class GetAttributesCallback extends FuseCallback { // When the OS wants to check if a node exists will try to get the attributes of it // so not founding them is not an error - Logger.info(`Attributes of ${this.name}:.`); + Logger.info(`No attributes found for ${error.path}`); return left(error); } @@ -88,6 +88,6 @@ export class GetAttributesCallback extends FuseCallback { const file = await this.container.filesSearcher.run({ path }); if (!file) { - return this.left(new FuseNoSuchFileOrDirectoryError()); + return this.left(new FuseNoSuchFileOrDirectoryError(path)); } try { diff --git a/src/apps/fuse/callbacks/ReadCallback.ts b/src/apps/fuse/callbacks/ReadCallback.ts index 7e9c4cc01..8d44dbc32 100644 --- a/src/apps/fuse/callbacks/ReadCallback.ts +++ b/src/apps/fuse/callbacks/ReadCallback.ts @@ -1,12 +1,15 @@ import Logger from 'electron-log'; -import fs from 'fs/promises'; import { VirtualDriveDependencyContainer } from '../dependency-injection/virtual-drive/VirtualDriveDependencyContainer'; +import { OfflineDriveDependencyContainer } from '../dependency-injection/offline/OfflineDriveDependencyContainer'; // eslint-disable-next-line @typescript-eslint/no-var-requires const fuse = require('@gcas/fuse'); export class ReadCallback { - constructor(private readonly container: VirtualDriveDependencyContainer) {} + constructor( + private readonly virtualDrive: VirtualDriveDependencyContainer, + private readonly offlineDrive: OfflineDriveDependencyContainer + ) {} private async read( filePath: string, @@ -14,18 +17,23 @@ export class ReadCallback { length: number, position: number ): Promise { - // Logger.debug('READING FILE FROM ', filePath, length, position); + Logger.debug('READING ', length, 'MB FROM ', filePath); - const data = await fs.readFile(filePath); + const readResult = await this.offlineDrive.contentsChunkReader.run( + filePath, + length, + position + ); - if (position >= data.length) { + if (!readResult.isPresent()) { Logger.debug('READ DONE'); return 0; } - const part = data.slice(position, position + length); - part.copy(buffer); // write the result of the read to the result buffer - return part.length; // number of bytes read + const chunk = readResult.get(); + + chunk.copy(buffer); // write the result of the read to the result buffer + return chunk.length; // number of bytes read } async execute( @@ -36,14 +44,14 @@ export class ReadCallback { pos: number, cb: (code: number, params?: any) => void ) { - const file = await this.container.filesSearcher.run({ path }); + const file = await this.virtualDrive.filesSearcher.run({ path }); if (!file) { cb(fuse.ENOENT); return; } - const filePath = this.container.relativePathToAbsoluteConverter.run( + const filePath = this.virtualDrive.relativePathToAbsoluteConverter.run( file.contentsId ); diff --git a/src/apps/fuse/callbacks/RenameOrMoveCallback.ts b/src/apps/fuse/callbacks/RenameOrMoveCallback.ts index 53f2a2be8..66722206b 100644 --- a/src/apps/fuse/callbacks/RenameOrMoveCallback.ts +++ b/src/apps/fuse/callbacks/RenameOrMoveCallback.ts @@ -36,6 +36,6 @@ export class RenameOrMoveCallback extends NotifyFuseCallback { return this.right(); } - return this.left(new FuseNoSuchFileOrDirectoryError()); + return this.left(new FuseNoSuchFileOrDirectoryError(src)); } } diff --git a/src/apps/fuse/callbacks/TrashFileCallback.ts b/src/apps/fuse/callbacks/TrashFileCallback.ts index bf4906421..96597cc6a 100644 --- a/src/apps/fuse/callbacks/TrashFileCallback.ts +++ b/src/apps/fuse/callbacks/TrashFileCallback.ts @@ -15,7 +15,7 @@ export class TrashFileCallback extends NotifyFuseCallback { }); if (!file) { - return this.left(new FuseNoSuchFileOrDirectoryError()); + return this.left(new FuseNoSuchFileOrDirectoryError(path)); } try { diff --git a/src/apps/fuse/dependency-injection/offline/OfflineContents/OfflineDriveDependencyContainer.ts b/src/apps/fuse/dependency-injection/offline/OfflineContents/OfflineDriveDependencyContainer.ts index 4492b6b84..73ecdfb98 100644 --- a/src/apps/fuse/dependency-injection/offline/OfflineContents/OfflineDriveDependencyContainer.ts +++ b/src/apps/fuse/dependency-injection/offline/OfflineContents/OfflineDriveDependencyContainer.ts @@ -1,3 +1,4 @@ +import { ContentsChunkReader } from '../../../../../context/offline-drive/contents/application/ContentsChunkReader'; import { OfflineContentsAppender } from '../../../../../context/offline-drive/contents/application/OfflineContentsAppender'; import { OfflineContentsCreator } from '../../../../../context/offline-drive/contents/application/OfflineContentsCreator'; import { OfflineContentsUploader } from '../../../../../context/offline-drive/contents/application/OfflineContentsUploader'; @@ -6,4 +7,5 @@ export interface OfflineContentsDependencyContainer { offlineContentsCreator: OfflineContentsCreator; offlineContentsAppender: OfflineContentsAppender; offlineContentsUploader: OfflineContentsUploader; + contentsChunkReader: ContentsChunkReader; } diff --git a/src/apps/fuse/dependency-injection/offline/OfflineContents/offlineContentsContainerBuilder.ts b/src/apps/fuse/dependency-injection/offline/OfflineContents/offlineContentsContainerBuilder.ts index 9d7513f19..cc64698ea 100644 --- a/src/apps/fuse/dependency-injection/offline/OfflineContents/offlineContentsContainerBuilder.ts +++ b/src/apps/fuse/dependency-injection/offline/OfflineContents/offlineContentsContainerBuilder.ts @@ -1,8 +1,10 @@ +import { ContentsChunkReader } from '../../../../../context/offline-drive/contents/application/ContentsChunkReader'; import { OfflineContentsAppender } from '../../../../../context/offline-drive/contents/application/OfflineContentsAppender'; import { OfflineContentsCreator } from '../../../../../context/offline-drive/contents/application/OfflineContentsCreator'; import { OfflineContentsUploader } from '../../../../../context/offline-drive/contents/application/OfflineContentsUploader'; import { EnvironmentOfflineContentsManagersFactory } from '../../../../../context/offline-drive/contents/infrastructure/EnvironmentRemoteFileContentsManagersFactory'; import { NodeFSOfflineContentsRepository } from '../../../../../context/offline-drive/contents/infrastructure/NodeFSOfflineContentsRepository'; +import { CachedFSContentsRepository } from '../../../../../context/offline-drive/contents/infrastructure/cache/CachedFSContentsRepository'; import { MainProcessUploadProgressTracker } from '../../../../../context/shared/infrastructure/MainProcessUploadProgressTracker'; import { FuseAppDataLocalFileContentsDirectoryProvider } from '../../../../../context/virtual-drive/shared/infrastructure/LocalFileContentsDirectoryProviders/FuseAppDataLocalFileContentsDirectoryProvider'; import { DependencyInjectionEventBus } from '../../../../fuse/dependency-injection/common/eventBus'; @@ -50,9 +52,13 @@ export async function buildOfflineContentsContainer( const offlineContentsCreator = new OfflineContentsCreator(repository); + const contentsRepository = new CachedFSContentsRepository(); + const contentsChunkReader = new ContentsChunkReader(contentsRepository); + return { offlineContentsCreator, offlineContentsAppender, offlineContentsUploader, + contentsChunkReader, }; } diff --git a/src/apps/fuse/index.ts b/src/apps/fuse/index.ts index 54fed8d9c..08fc1b2d6 100644 --- a/src/apps/fuse/index.ts +++ b/src/apps/fuse/index.ts @@ -5,18 +5,11 @@ import eventBus from '../main/event-bus'; import { FuseApp } from './FuseApp'; import path from 'path'; import { FuseDependencyContainerFactory } from './dependency-injection/FuseDependencyContainerFactory'; -import { HydrationApi } from '../hydration-api/HydrationApi'; import { getRootVirtualDrive } from '../main/virtual-root-folder/service'; let fuseApp: FuseApp; async function startFuseApp() { - const api = new HydrationApi(); - - await api.start({ - debug: false, - }); - const root = getRootVirtualDrive(); Logger.debug('ROOT FOLDER: ', root); diff --git a/src/context/offline-drive/contents/application/ContentsChunkReader.ts b/src/context/offline-drive/contents/application/ContentsChunkReader.ts new file mode 100644 index 000000000..a179e219e --- /dev/null +++ b/src/context/offline-drive/contents/application/ContentsChunkReader.ts @@ -0,0 +1,22 @@ +import { Optional } from '../../../../shared/types/Optional'; +import { ContentsRepository } from '../domain/ContentsRepository'; + +export class ContentsChunkReader { + constructor(private readonly repository: ContentsRepository) {} + + async run( + contentsPath: string, + length: number, + position: number + ): Promise> { + const data = await this.repository.read(contentsPath); + + if (position >= data.length) { + return Optional.empty(); + } + + const chunk = data.slice(position, position + length); + + return Optional.of(chunk); + } +} diff --git a/src/context/offline-drive/contents/domain/ContentsRepository.ts b/src/context/offline-drive/contents/domain/ContentsRepository.ts new file mode 100644 index 000000000..97649d3b6 --- /dev/null +++ b/src/context/offline-drive/contents/domain/ContentsRepository.ts @@ -0,0 +1,5 @@ +export interface ContentsRepository { + read(path: string): Promise; + + forget(path: string): void; +} diff --git a/src/context/offline-drive/contents/infrastructure/cache/CachedFSContentsRepository.ts b/src/context/offline-drive/contents/infrastructure/cache/CachedFSContentsRepository.ts new file mode 100644 index 000000000..b648db88a --- /dev/null +++ b/src/context/offline-drive/contents/infrastructure/cache/CachedFSContentsRepository.ts @@ -0,0 +1,23 @@ +import { ContentsRepository } from '../../domain/ContentsRepository'; +import fs from 'fs/promises'; + +export class CachedFSContentsRepository implements ContentsRepository { + private buffers: Map = new Map(); + + async read(path: string): Promise { + const cached = this.buffers.get(path); + + if (cached) { + return cached; + } + + const read = await fs.readFile(path); + this.buffers.set(path, read); + + return read; + } + + forget(_path: string): void { + // + } +} diff --git a/src/shared/types/Optional.ts b/src/shared/types/Optional.ts new file mode 100644 index 000000000..ddd0bcfdf --- /dev/null +++ b/src/shared/types/Optional.ts @@ -0,0 +1,23 @@ +export class Optional { + private constructor(private readonly value: T | undefined) {} + + static of(value: T): Optional { + return new Optional(value); + } + + static empty(): Optional { + return new Optional(undefined); + } + + get(): T { + if (!this.value) { + throw new Error('Element not found'); + } + + return this.value; + } + + isPresent(): boolean { + return this.value !== undefined; + } +}