From 8ffaf642929a007ab7d7d6712bbed5431267a9ab Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 8 Apr 2022 18:29:05 -0500 Subject: [PATCH] fileStorage: add file create option When opening a file, sometimes we want to create it if it does not exist and sometimes we don't. --- src/editor/sagas.test.ts | 2 +- src/editor/sagas.ts | 2 +- src/fileStorage/actions.ts | 14 ++++++---- src/fileStorage/sagas.test.ts | 48 +++++++++++++++++++---------------- src/fileStorage/sagas.ts | 11 +++++--- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/editor/sagas.test.ts b/src/editor/sagas.test.ts index a985e042..634a6821 100644 --- a/src/editor/sagas.test.ts +++ b/src/editor/sagas.test.ts @@ -82,7 +82,7 @@ describe('per-editor sagas', () => { saga.put(editorOpenFile('test.file')); await expect(saga.take()).resolves.toEqual( - fileStorageOpen('test.file', 'w'), + fileStorageOpen('test.file', 'w', false), ); }); diff --git a/src/editor/sagas.ts b/src/editor/sagas.ts index 585841c4..aabb7533 100644 --- a/src/editor/sagas.ts +++ b/src/editor/sagas.ts @@ -78,7 +78,7 @@ function* handleEditorOpenFile( let closeRequested = false; try { - yield* put(fileStorageOpen(action.fileName, 'w')); + yield* put(fileStorageOpen(action.fileName, 'w', false)); const { didOpen, didFailToOpen } = yield* race({ didOpen: take(fileStorageDidOpen.when((a) => a.path === action.fileName)), diff --git a/src/fileStorage/actions.ts b/src/fileStorage/actions.ts index ee70a6a4..deba22ea 100644 --- a/src/fileStorage/actions.ts +++ b/src/fileStorage/actions.ts @@ -81,12 +81,16 @@ export const fileStorageDidRemoveItem = createAction((file: FileMetadata) => ({ * Action that requests to open a file in storage. * @param path The file path. * @param mode 'r' to open for reading or 'w' to open for writing. + * @param create If true, create the file if it does not exist. */ -export const fileStorageOpen = createAction((path: string, mode: FileOpenMode) => ({ - type: 'fileStorage.action.Open', - path, - mode, -})); +export const fileStorageOpen = createAction( + (path: string, mode: FileOpenMode, create: boolean) => ({ + type: 'fileStorage.action.Open', + path, + mode, + create, + }), +); /** * Action that indicates that {@link fileStorageOpen} succeeded. diff --git a/src/fileStorage/sagas.test.ts b/src/fileStorage/sagas.test.ts index 32c4b8a8..44172f62 100644 --- a/src/fileStorage/sagas.test.ts +++ b/src/fileStorage/sagas.test.ts @@ -89,7 +89,7 @@ async function setUpTestFile(saga: AsyncSaga): Promise<[FileMetadata, string]> { const emptyFile: FileMetadata = { ...testFile, sha256: emptyFileSha256 }; - saga.put(fileStorageOpen(testFilePath, 'w')); + saga.put(fileStorageOpen(testFilePath, 'w', true)); const didOpen = await saga.take(); @@ -165,7 +165,7 @@ describe('open', () => { }); it('should fail to open for reading if file does not exist', async () => { - saga.put(fileStorageOpen('test.file', 'r')); + saga.put(fileStorageOpen('test.file', 'r', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidFailToOpen( @@ -177,7 +177,7 @@ describe('open', () => { describe('should open file for writing if file does not exist', () => { beforeEach(async () => { - saga.put(fileStorageOpen('test.file', 'w')); + saga.put(fileStorageOpen('test.file', 'w', true)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 0 as FD), @@ -194,7 +194,7 @@ describe('open', () => { describe('should fail to open if file is already open for writing', () => { it.each(['r', 'w'])('mode: %o', async (mode) => { - saga.put(fileStorageOpen('test.file', mode)); + saga.put(fileStorageOpen('test.file', mode, true)); await expect(saga.take()).resolves.toEqual( fileStorageDidFailToOpen( @@ -218,7 +218,7 @@ describe('open', () => { }); it.each(['r', 'w'])('mode: %o', async (mode) => { - saga.put(fileStorageOpen('test.file', mode)); + saga.put(fileStorageOpen('test.file', mode, false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -229,13 +229,13 @@ describe('open', () => { it('should allow multiple readers', async () => { await setUpTestFile(saga); - saga.put(fileStorageOpen('test.file', 'r')); + saga.put(fileStorageOpen('test.file', 'r', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), ); - saga.put(fileStorageOpen('test.file', 'r')); + saga.put(fileStorageOpen('test.file', 'r', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 2 as FD), @@ -245,13 +245,13 @@ describe('open', () => { it('should fail to open for writing if already open for reading', async () => { await setUpTestFile(saga); - saga.put(fileStorageOpen('test.file', 'r')); + saga.put(fileStorageOpen('test.file', 'r', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), ); - saga.put(fileStorageOpen('test.file', 'w')); + saga.put(fileStorageOpen('test.file', 'w', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidFailToOpen( @@ -262,7 +262,7 @@ describe('open', () => { }); it('should allow calling close multiple times', async () => { - saga.put(fileStorageOpen('test.file', 'w')); + saga.put(fileStorageOpen('test.file', 'w', true)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 0 as FD), @@ -302,7 +302,7 @@ describe('read', () => { it.each(['r', 'w'])('mode: %o', async (mode) => { const [, contents] = await setUpTestFile(saga); - saga.put(fileStorageOpen('test.file', mode)); + saga.put(fileStorageOpen('test.file', mode, false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -320,7 +320,7 @@ describe('read', () => { it.each(['r', 'w'])('mode: %o', async (mode) => { await setUpTestFile(saga); - saga.put(fileStorageOpen('test.file', mode)); + saga.put(fileStorageOpen('test.file', mode, false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -357,7 +357,7 @@ describe('write', () => { it('should write a file open for writing', async () => { await setUpTestFile(saga); - saga.put(fileStorageOpen('test.file', 'w')); + saga.put(fileStorageOpen('test.file', 'w', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -371,7 +371,7 @@ describe('write', () => { it('should fail to write a file open for reading', async () => { await setUpTestFile(saga); - saga.put(fileStorageOpen('test.file', 'r')); + saga.put(fileStorageOpen('test.file', 'r', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -391,7 +391,7 @@ describe('write', () => { it.each(['r', 'w'])('mode: %o', async (mode) => { await setUpTestFile(saga); - saga.put(fileStorageOpen('test.file', mode)); + saga.put(fileStorageOpen('test.file', mode, false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -426,7 +426,9 @@ describe('readFile', () => { saga.put(fileStorageReadFile('test.file')); - await expect(saga.take()).resolves.toEqual(fileStorageOpen('test.file', 'r')); + await expect(saga.take()).resolves.toEqual( + fileStorageOpen('test.file', 'r', false), + ); }); it('should forward open error', async () => { @@ -491,7 +493,9 @@ describe('writeFile', () => { saga.put(fileStorageWriteFile('test.file', contents)); - await expect(saga.take()).resolves.toEqual(fileStorageOpen('test.file', 'w')); + await expect(saga.take()).resolves.toEqual( + fileStorageOpen('test.file', 'w', true), + ); }); it('should forward open error', async () => { @@ -570,7 +574,7 @@ describe('copyFile', () => { }); it('should fail if new file is open', async () => { - saga.put(fileStorageOpen('new.file', 'w')); + saga.put(fileStorageOpen('new.file', 'w', true)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('new.file', 1 as FD), ); @@ -586,7 +590,7 @@ describe('copyFile', () => { }); it('should fail if new file exists', async () => { - saga.put(fileStorageOpen('new.file', 'w')); + saga.put(fileStorageOpen('new.file', 'w', true)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('new.file', 1 as FD), ); @@ -642,7 +646,7 @@ describe('deleteFile', () => { }); it('should fail if file is open', async () => { - saga.put(fileStorageOpen('test.file', 'r')); + saga.put(fileStorageOpen('test.file', 'r', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -696,7 +700,7 @@ describe('renameFile', () => { }); it('should fail if file is open', async () => { - saga.put(fileStorageOpen('test.file', 'r')); + saga.put(fileStorageOpen('test.file', 'r', false)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen('test.file', 1 as FD), @@ -715,7 +719,7 @@ describe('renameFile', () => { // there are two paths here that result in the same error // the first is if the file is open, e.g. in another tab - saga.put(fileStorageOpen(newPath, 'w')); + saga.put(fileStorageOpen(newPath, 'w', true)); await expect(saga.take()).resolves.toEqual( fileStorageDidOpen(newPath, 1 as FD), diff --git a/src/fileStorage/sagas.ts b/src/fileStorage/sagas.ts index 7acf8236..26ccad5e 100644 --- a/src/fileStorage/sagas.ts +++ b/src/fileStorage/sagas.ts @@ -233,8 +233,8 @@ function* handleOpen( return metadata.uuid; } - // if reading, do not create a new file - if (action.mode === 'r') { + // don't create a new file if we were not asked to + if (!action.create) { return; } @@ -394,7 +394,7 @@ function* handleWrite( */ function* handleReadFile(action: ReturnType): Generator { try { - yield* put(fileStorageOpen(action.path, 'r')); + yield* put(fileStorageOpen(action.path, 'r', false)); const { didOpen, didFailToOpen } = yield* race({ didOpen: take(fileStorageDidOpen.when((a) => a.path === action.path)), @@ -441,11 +441,14 @@ function* handleReadFile(action: ReturnType): Genera /** * Handle open, write, close action. + * + * If the file does not exist, a new file will be created. + * * @param action The action that triggered this saga. */ function* handleWriteFile(action: ReturnType): Generator { try { - yield* put(fileStorageOpen(action.path, 'w')); + yield* put(fileStorageOpen(action.path, 'w', true)); const { didOpen, didFailToOpen } = yield* race({ didOpen: take(fileStorageDidOpen.when((a) => a.path === action.path)),