import { constVoid, Endomorphism, pipe } from 'fp-ts/function'
import * as J from 'fp-ts/Json'
import * as R from 'fp-ts/Random'
import * as RNEA from 'fp-ts/ReadonlyNonEmptyArray'
import * as TE from 'fp-ts/TaskEither'
import * as t from 'io-ts'
import { memory } from './cache/Memory'
import { storage } from './cache/Storage'
import * as $L from './Log'
import * as $R from './Random'
export interface Cache {
readonly get: (
key: string,
codec: t.Type,
) => TE.TaskEither
readonly set: (
key: string,
codec: t.Type,
ttl?: number,
) => (value: A) => TE.TaskEither
readonly delete: (key: string) => TE.TaskEither
readonly clear: TE.TaskEither
}
export const chain = (...caches: RNEA.ReadonlyNonEmptyArray): Cache => ({
get: (key, codec) =>
pipe(
caches,
RNEA.reduce(TE.left(Error()), (value, { get }) =>
TE.Alt.alt(value, () => get(key, codec)),
),
),
set: (key, codec, ttl) => (value) =>
pipe(
caches,
TE.traverseArray(({ set }) => set(key, codec, ttl)(value)),
TE.map(constVoid),
),
delete: (key) =>
pipe(
caches,
TE.traverseArray(({ delete: _delete }) => _delete(key)),
TE.map(constVoid),
),
clear: pipe(
caches,
TE.traverseArray(({ clear }) => clear),
TE.map(constVoid),
),
})
// eslint-disable-next-line @typescript-eslint/unified-signatures
export function log(logStart: $L.Logger, logEnd: $L.Logger): Endomorphism
export function log(log: $L.Logger): Endomorphism
export function log(log0: $L.Logger, log1?: $L.Logger) {
const logStart = undefined !== log1 ? log0 : $L.void
const logEnd = log1 || log0
return (cache: Cache): Cache => ({
get: (key, codec) =>
$R.salt(TE.MonadIO)(R.randomInt(0, Number.MAX_SAFE_INTEGER), (salt) =>
pipe(
logStart(`[${salt}] \rItem "${key}" retrieved from cache`),
TE.fromIO,
TE.chain(() => cache.get(key, codec)),
TE.chainFirstIOK(() =>
logEnd(`[${salt}] \rItem "${key}" retrieved from cache`),
),
),
),
set: (key, codec, ttl) => (value) =>
$R.salt(TE.MonadIO)(R.randomInt(0, Number.MAX_SAFE_INTEGER), (salt) =>
pipe(
logStart(`[${salt}] \rItem "${key}" saved to cache`),
TE.fromIO,
TE.chain(() => cache.set(key, codec, ttl)(value)),
TE.chainFirstIOK(() =>
logEnd(`[${salt}] \rItem "${key}" saved to cache`),
),
),
),
delete: (key) =>
$R.salt(TE.MonadIO)(R.randomInt(0, Number.MAX_SAFE_INTEGER), (salt) =>
pipe(
logStart(`[${salt}] \rItem "${key}" deleted from cache`),
TE.fromIO,
TE.chain(() => cache.delete(key)),
TE.chainFirstIOK(() =>
logEnd(`[${salt}] \rItem "${key}" deleted from cache`),
),
),
),
clear: $R.salt(TE.MonadIO)(
R.randomInt(0, Number.MAX_SAFE_INTEGER),
(salt) =>
pipe(
logStart(`[${salt}] \rCache cleared`),
TE.fromIO,
TE.chain(() => cache.clear),
TE.chainFirstIOK(() => logEnd(`[${salt}] \rCache cleared`)),
),
),
})
}
export { memory, storage }