diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/either.ts | 25 | ||||
-rw-r--r-- | utils/mod.ts | 1 | ||||
-rw-r--r-- | utils/run.ts | 28 | ||||
-rw-r--r-- | utils/trace.ts | 47 |
4 files changed, 75 insertions, 26 deletions
diff --git a/utils/either.ts b/utils/either.ts new file mode 100644 index 0000000..d21c796 --- /dev/null +++ b/utils/either.ts @@ -0,0 +1,25 @@ +export interface IEither<E, T> { + ok?: T; + err?: E; + mapBoth: <Ee, Tt>( + errBranch: (e: E) => Ee, + okBranch: (o: T) => Tt, + ) => IEither<Ee, Tt>; +} + +export class Either<E, T> implements IEither<E, T> { + private constructor(readonly err?: E, readonly ok?: T) {} + + public mapBoth<Ee, Tt>(errBranch: (e: E) => Ee, okBranch: (t: T) => Tt) { + if (this.err) return new Either<Ee, Tt>(errBranch(this.err)); + return new Either<Ee, Tt>(undefined, okBranch(this.ok!)); + } + + static left<E, T>(e: E) { + return new Either<E, T>(e); + } + + static right<E, T>(t: T) { + return new Either<E, T>(undefined, t); + } +} diff --git a/utils/mod.ts b/utils/mod.ts index a8a0751..53ea173 100644 --- a/utils/mod.ts +++ b/utils/mod.ts @@ -1,4 +1,5 @@ export * from "./trace.ts"; +export * from "./either.ts"; export * from "./env.ts"; export * from "./run.ts"; export * from "./secret.ts"; diff --git a/utils/run.ts b/utils/run.ts index 60ae1e6..06e7d9f 100644 --- a/utils/run.ts +++ b/utils/run.ts @@ -1,7 +1,10 @@ +import { Either } from "./mod.ts"; + +export class ProcessError extends Error {} export const getStdout = async ( cmd: string[] | string, options: Deno.CommandOptions = {}, -): Promise<string> => { +): Promise<Either<ProcessError, string>> => { const [exec, ...args] = (typeof cmd === "string") ? cmd.split(" ") : cmd; const command = new Deno.Command(exec, { args, @@ -10,12 +13,21 @@ export const getStdout = async ( ...options, }); - const { code, stdout, stderr } = await command.output(); - - const stdoutText = new TextDecoder().decode(stdout); - const stderrText = new TextDecoder().decode(stderr); - - if (code !== 0) throw new Error(`command failed\n${stderrText}`); + try { + const { code, stdout, stderr } = await command.output(); + const stdoutText = new TextDecoder().decode(stdout); + const stderrText = new TextDecoder().decode(stderr); - return stdoutText; + if (code !== 0) { + return Either.left<ProcessError, string>( + new ProcessError(`command failed\n${stderrText}`), + ); + } + return Either.right<ProcessError, string>(stdoutText); + } catch (e) { + if (e instanceof Error) { + return Either.left<ProcessError, string>(e); + } + throw new Error("unknown error " + e); + } }; diff --git a/utils/trace.ts b/utils/trace.ts index eb4ac2f..373f37e 100644 --- a/utils/trace.ts +++ b/utils/trace.ts @@ -25,12 +25,15 @@ export interface ITraceable<T, L extends ITraceableLogger<L>> { mapper: ITraceableMapper<T, L, ITraceableTuple<U>>, ) => ITraceable<U, L>; peek: (peek: ITraceableMapper<T, L, void>) => ITraceable<T, L>; - flatMap: <U>(mapper: ITraceableMapper<T, L, ITraceable<U, L>>) => ITraceable<U, L>; - flatMapAsync<U>(mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>): ITraceable<Promise<U>, L>; + flatMap: <U>( + mapper: ITraceableMapper<T, L, ITraceable<U, L>>, + ) => ITraceable<U, L>; + flatMapAsync<U>( + mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>, + ): ITraceable<Promise<U>, L>; } -export class TraceableLogger - implements ITraceableLogger<TraceableLogger> { +export class TraceableLogger implements ITraceableLogger<TraceableLogger> { private readonly logger: Logger = console; constructor( private readonly traces = [() => `[${new Date().toISOString()}]`], @@ -62,23 +65,30 @@ export class TraceableLogger } } -export class TraceableImpl< +class TraceableImpl< T, L extends ITraceableLogger<L>, > implements ITraceable<T, L> { - private constructor(readonly item: T, readonly logger: L) {} + protected constructor(readonly item: T, readonly logger: L) {} public map<U>(mapper: ITraceableMapper<T, L, U>) { const result = mapper(this); return new TraceableImpl(result, this.logger); } - public flatMap<U>(mapper: ITraceableMapper<T, L, ITraceable<U, L>>): ITraceable<U, L> { + public flatMap<U>( + mapper: ITraceableMapper<T, L, ITraceable<U, L>>, + ): ITraceable<U, L> { return mapper(this); } - public flatMapAsync<U>(mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>): ITraceable<Promise<U>, L> { - return new TraceableImpl(mapper(this).then(({ item }) => item), this.logger); + public flatMapAsync<U>( + mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>, + ): ITraceable<Promise<U>, L> { + return new TraceableImpl( + mapper(this).then(({ item }) => item), + this.logger, + ); } public peek(peek: ITraceableMapper<T, L, void>) { @@ -95,20 +105,21 @@ export class TraceableImpl< mapper: ITraceableMapper<T, L, U>, ): ITraceableMapper<Promise<T>, L, Promise<U>> { return (traceablePromise) => - traceablePromise.flatMapAsync(async (t) => { - const item = await t.item; - return t.map(() => item).map(mapper); - }).item; + traceablePromise.flatMapAsync(async (t) => { + const item = await t.item; + return t.map(() => item).map(mapper); + }).item; } +} - static withClassTrace<C extends Object, T, L extends ITraceableLogger<L>>(c: C): ITraceableMapper<T, L, ITraceableTuple<T>> { +export class Traceable<T> extends TraceableImpl<T, TraceableLogger> { + static withClassTrace<C extends object, T>( + c: C, + ): ITraceableMapper<T, TraceableLogger, ITraceableTuple<T>> { return (t) => [t.item, () => c.constructor.name]; } static from<T>(t: T) { - return new TraceableImpl(t, new TraceableLogger()); + return new Traceable(t, new TraceableLogger()); } } - -export interface Traceable<T, L extends ITraceableLogger<L> = TraceableLogger> extends ITraceable<T, L> { -} |