From e49fda41176d025a671802be76c219d66167276f Mon Sep 17 00:00:00 2001 From: Elizabeth Alexander Hunt Date: Mon, 12 May 2025 23:05:27 -0700 Subject: snapshot --- utils/either.ts | 25 +++++++++++++++++++++++++ utils/mod.ts | 1 + utils/run.ts | 28 ++++++++++++++++++++-------- utils/trace.ts | 47 +++++++++++++++++++++++++++++------------------ 4 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 utils/either.ts (limited to 'utils') 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 { + ok?: T; + err?: E; + mapBoth: ( + errBranch: (e: E) => Ee, + okBranch: (o: T) => Tt, + ) => IEither; +} + +export class Either implements IEither { + private constructor(readonly err?: E, readonly ok?: T) {} + + public mapBoth(errBranch: (e: E) => Ee, okBranch: (t: T) => Tt) { + if (this.err) return new Either(errBranch(this.err)); + return new Either(undefined, okBranch(this.ok!)); + } + + static left(e: E) { + return new Either(e); + } + + static right(t: T) { + return new Either(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 => { +): Promise> => { 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( + new ProcessError(`command failed\n${stderrText}`), + ); + } + return Either.right(stdoutText); + } catch (e) { + if (e instanceof Error) { + return Either.left(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> { mapper: ITraceableMapper>, ) => ITraceable; peek: (peek: ITraceableMapper) => ITraceable; - flatMap: (mapper: ITraceableMapper>) => ITraceable; - flatMapAsync(mapper: ITraceableMapper>>): ITraceable, L>; + flatMap: ( + mapper: ITraceableMapper>, + ) => ITraceable; + flatMapAsync( + mapper: ITraceableMapper>>, + ): ITraceable, L>; } -export class TraceableLogger - implements ITraceableLogger { +export class TraceableLogger implements ITraceableLogger { 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, > implements ITraceable { - private constructor(readonly item: T, readonly logger: L) {} + protected constructor(readonly item: T, readonly logger: L) {} public map(mapper: ITraceableMapper) { const result = mapper(this); return new TraceableImpl(result, this.logger); } - public flatMap(mapper: ITraceableMapper>): ITraceable { + public flatMap( + mapper: ITraceableMapper>, + ): ITraceable { return mapper(this); } - public flatMapAsync(mapper: ITraceableMapper>>): ITraceable, L> { - return new TraceableImpl(mapper(this).then(({ item }) => item), this.logger); + public flatMapAsync( + mapper: ITraceableMapper>>, + ): ITraceable, L> { + return new TraceableImpl( + mapper(this).then(({ item }) => item), + this.logger, + ); } public peek(peek: ITraceableMapper) { @@ -95,20 +105,21 @@ export class TraceableImpl< mapper: ITraceableMapper, ): ITraceableMapper, L, Promise> { 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: C): ITraceableMapper> { +export class Traceable extends TraceableImpl { + static withClassTrace( + c: C, + ): ITraceableMapper> { return (t) => [t.item, () => c.constructor.name]; } static from(t: T) { - return new TraceableImpl(t, new TraceableLogger()); + return new Traceable(t, new TraceableLogger()); } } - -export interface Traceable = TraceableLogger> extends ITraceable { -} -- cgit v1.2.3-70-g09d2