import { sequenceT } from 'fp-ts/lib/Apply' ; import * as C from 'fp-ts/lib/Console' ; import * as E from 'fp-ts/lib/Either' ; import { absurd } from 'fp-ts/lib/function' ; import { pipe } from 'fp-ts/lib/pipeable' ; import * as RTE from 'fp-ts/lib/ReaderTaskEither' ; import * as T from 'fp-ts/lib/Task' ; import * as TE from 'fp-ts/lib/TaskEither' ; import { promises as fs } from 'fs' ; import { getDefaultContext } from 'io-ts' ; import { PathReporter } from 'io-ts/lib/PathReporter' ; import { mergeDeepRight } from 'ramda' ; import { Config , ConfigPartial , TConfig } from '../episode4/types' ; import { FileReadingError , MissingEnvVariableError , ValidationError } from './errors' ; import TaskEither = TE . TaskEither ; import ReaderTaskEither = RTE . ReaderTaskEither ; const defaultContext = getDefaultContext ( TConfig ) ; const seqE = sequenceT ( E . either ) ; export const readEnv = ( env ) => TE . fromIOEither ( ( ) => seqE ( E . fromNullable ( new MissingEnvVariableError ( 'COMMON was not defined' ) ) ( env . COMMON ) , E . fromNullable ( new MissingEnvVariableError ( 'DEV was not defined' ) ) ( env . DEV ) ) ) ; export const read = ( paths ) => TE . tryCatch ( ( ) => Promise . all < ConfigPartial > ( paths . map ( async ( path ) => { const configStr = await fs . readFile ( path , { encoding : 'utf8' } ) ; return JSON . parse ( configStr ) ; } ) ) , ( reason ) => new FileReadingError ( String ( reason ) ) ) ; export const validatedConfig = pipe ( read , RTE . map ( ( partials ) => partials . reduce ( mergeDeepRight , { } ) ) , RTE . chain ( ( config ) => RTE . fromEither ( pipe ( TConfig . validate ( config , defaultContext ) , E . mapLeft ( ( errs ) => new ValidationError ( PathReporter . report ( E . left ( errs ) ) . join ( '\n' ) ) ) ) ) ) ) ; export const logEnv = ( config ) => TE . rightIO ( C . log ( config ) ) ; export const startServer = ( _config ) => TE . tryCatch ( async ( ) => { while ( true ) { } } , ( reason ) => new Error ( String ( reason ) ) ) ; export const logAndStart = pipe ( logEnv , RTE . chain ( ( ) => startServer ) ) ; export const exit = async ( ) => E . right ( process . exit ( 1 ) ) ; export const program = ( env ) => pipe ( readEnv ( env ) , TE . chain ( validatedConfig ) , TE . chain ( logAndStart ) , TE . fold ( ( err ) => pipe ( TE . rightIO ( C . log ( err . message ) ) , TE . chain ( ( ) => { switch ( true ) { case err instanceof FileReadingError : case err instanceof ValidationError : case err instanceof MissingEnvVariableError : return exit ; default : return T . delay ( 1000 ) ( program ( env ) ) ; } } ) ) , absurd ) ) ;