From e32a57d07dc05c8275180e59cff59cd76cbd4841 Mon Sep 17 00:00:00 2001
From: streamich <streamich@gmail.com>
Date: Tue, 19 Mar 2024 11:55:09 +0100
Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20allow=20to=20customize?=
 =?UTF-8?q?=20CAS=20storage=20hash=20and=20location=20mappin?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/cas/types.ts                       | 10 ++++----
 src/crud-to-cas/CrudCas.ts             | 35 ++++++++++++++------------
 src/crud-to-cas/__tests__/testCasfs.ts |  2 +-
 3 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/src/cas/types.ts b/src/cas/types.ts
index 6b2671039..9ec8d7866 100644
--- a/src/cas/types.ts
+++ b/src/cas/types.ts
@@ -1,10 +1,10 @@
 import type { CrudResourceInfo } from '../crud/types';
 
-export interface CasApi {
-  put(blob: Uint8Array): Promise<string>;
-  get(hash: string, options?: CasGetOptions): Promise<Uint8Array>;
-  del(hash: string, silent?: boolean): Promise<void>;
-  info(hash: string): Promise<CrudResourceInfo>;
+export interface CasApi<Hash> {
+  put(blob: Uint8Array): Promise<Hash>;
+  get(hash: Hash, options?: CasGetOptions): Promise<Uint8Array>;
+  del(hash: Hash, silent?: boolean): Promise<void>;
+  info(hash: Hash): Promise<CrudResourceInfo>;
 }
 
 export interface CasGetOptions {
diff --git a/src/crud-to-cas/CrudCas.ts b/src/crud-to-cas/CrudCas.ts
index a9fc8e01e..75ea6526b 100644
--- a/src/crud-to-cas/CrudCas.ts
+++ b/src/crud-to-cas/CrudCas.ts
@@ -1,9 +1,11 @@
-import { hashToLocation } from './util';
+import { hashToLocation as defaultHashToLocation } from './util';
 import type { CasApi, CasGetOptions } from '../cas/types';
 import type { CrudApi, CrudResourceInfo } from '../crud/types';
+import type { FsLocation } from '../fsa-to-node/types';
 
-export interface CrudCasOptions {
-  hash: (blob: Uint8Array) => Promise<string>;
+export interface CrudCasOptions<Hash = string> {
+  hash: (blob: Uint8Array) => Promise<Hash>;
+  hash2Loc?: (hash: Hash) => FsLocation;
 }
 
 const normalizeErrors = async <T>(code: () => Promise<T>): Promise<T> => {
@@ -21,21 +23,22 @@ const normalizeErrors = async <T>(code: () => Promise<T>): Promise<T> => {
   }
 };
 
-export class CrudCas implements CasApi {
-  constructor(
-    protected readonly crud: CrudApi,
-    protected readonly options: CrudCasOptions,
-  ) {}
+export class CrudCas<Hash = string> implements CasApi<Hash> {
+  protected readonly hash2Loc: (hash: Hash) => FsLocation;
 
-  public readonly put = async (blob: Uint8Array): Promise<string> => {
+  constructor(protected readonly crud: CrudApi, protected readonly options: CrudCasOptions<Hash>) {
+    this.hash2Loc = options.hash2Loc || <(hash: Hash) => FsLocation>defaultHashToLocation;
+  }
+
+  public readonly put = async (blob: Uint8Array): Promise<Hash> => {
     const digest = await this.options.hash(blob);
-    const [collection, resource] = hashToLocation(digest);
+    const [collection, resource] = this.hash2Loc(digest);
     await this.crud.put(collection, resource, blob);
     return digest;
   };
 
-  public readonly get = async (hash: string, options?: CasGetOptions): Promise<Uint8Array> => {
-    const [collection, resource] = hashToLocation(hash);
+  public readonly get = async (hash: Hash, options?: CasGetOptions): Promise<Uint8Array> => {
+    const [collection, resource] = this.hash2Loc(hash);
     return await normalizeErrors(async () => {
       const blob = await this.crud.get(collection, resource);
       if (!options?.skipVerification) {
@@ -46,15 +49,15 @@ export class CrudCas implements CasApi {
     });
   };
 
-  public readonly del = async (hash: string, silent?: boolean): Promise<void> => {
-    const [collection, resource] = hashToLocation(hash);
+  public readonly del = async (hash: Hash, silent?: boolean): Promise<void> => {
+    const [collection, resource] = this.hash2Loc(hash);
     await normalizeErrors(async () => {
       return await this.crud.del(collection, resource, silent);
     });
   };
 
-  public readonly info = async (hash: string): Promise<CrudResourceInfo> => {
-    const [collection, resource] = hashToLocation(hash);
+  public readonly info = async (hash: Hash): Promise<CrudResourceInfo> => {
+    const [collection, resource] = this.hash2Loc(hash);
     return await normalizeErrors(async () => {
       return await this.crud.info(collection, resource);
     });
diff --git a/src/crud-to-cas/__tests__/testCasfs.ts b/src/crud-to-cas/__tests__/testCasfs.ts
index 16c7f5cda..e71f4b4fd 100644
--- a/src/crud-to-cas/__tests__/testCasfs.ts
+++ b/src/crud-to-cas/__tests__/testCasfs.ts
@@ -16,7 +16,7 @@ const b = (str: string) => {
 };
 
 export type Setup = () => {
-  cas: CasApi;
+  cas: CasApi<string>;
   crud: CrudApi;
   snapshot: () => Record<string, string | null>;
 };