Skip to content

Instantly share code, notes, and snippets.

@tstelzer
Created March 11, 2022 22:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tstelzer/8d4fe28a8a9758794386640faeb4cc59 to your computer and use it in GitHub Desktop.
Save tstelzer/8d4fe28a8a9758794386640faeb4cc59 to your computer and use it in GitHub Desktop.
alternative effec-ts/node/Runtime to print JSON instead of raw text
import type {CustomRuntime} from '@effect-ts/core/Effect';
import * as T from '@effect-ts/core/Effect';
import {defaultRuntime} from '@effect-ts/core/Effect';
import * as Cause from '@effect-ts/system/Cause';
import * as Pretty from '@effect-ts/system/Cause/Pretty';
import {AtomicBoolean} from '@effect-ts/system/Support/AtomicBoolean';
import * as N from '@effect-ts/node/Runtime';
import * as IO from '@effect-ts/core/IO';
import * as A from '@effect-ts/core/Collections/Immutable/Array';
function format(segment: Pretty.Segment): readonly string[] {
switch (segment._tag) {
case 'Failure': {
return segment.lines;
}
case 'Parallel': {
return A.reduceRight_<Pretty.Sequential, string[]>(
segment.all,
[],
(current, acc) => [...acc, ...format(current)],
);
}
case 'Sequential': {
return A.chain_(segment.all, seg => format(seg));
}
}
}
function prettyLines<E>(
cause: Cause.Cause<E>,
renderer: Pretty.Renderer<E>,
): IO.IO<A.Array<string>> {
return IO.gen(function* (_) {
const s = yield* _(Pretty.causeToSequential(cause, renderer));
if (s.all.length === 1 && s.all[0] && s.all[0]._tag === 'Failure') {
return s.all[0].lines;
}
return format(s);
});
}
function prettyM<E1>(
cause: Cause.Cause<E1>,
renderer: Pretty.Renderer<E1>,
): IO.IO<string> {
return IO.gen(function* (_) {
const lines = yield* _(prettyLines(cause, renderer));
return lines.join('\n');
});
}
const pretty = <E1>(
cause: Cause.Cause<E1>,
renderer: Pretty.Renderer<E1> = Pretty.defaultRenderer,
): string => IO.run(prettyM(cause, renderer));
class NodeRuntime<R, X> {
constructor(readonly custom: CustomRuntime<R, X>) {
this.runMain = this.runMain.bind(this);
}
runMain<E, A>(
effect: T.Effect<R, E, A>,
customHook: (
cont: NodeJS.SignalsListener,
) => NodeJS.SignalsListener = N.defaultHook,
customTeardown: typeof N.defaultTeardown = N.defaultTeardown,
): void {
const onExit = (s: number): never => {
process.exit(s);
};
const context = this.custom.runFiber(effect);
context.runAsync(exit => {
switch (exit._tag) {
case 'Failure': {
if (Cause.interruptedOnly(exit.cause)) {
customTeardown(0, context.id, onExit);
break;
} else {
console.error(
JSON.stringify({
message: pretty(
exit.cause,
this.custom.platform.value.renderer,
),
}),
);
customTeardown(1, context.id, onExit);
break;
}
}
case 'Success': {
customTeardown(0, context.id, onExit);
break;
}
}
});
const interrupted = new AtomicBoolean(false);
const handler: NodeJS.SignalsListener = signal => {
customHook(() => {
process.removeListener('SIGTERM', handler);
process.removeListener('SIGINT', handler);
if (interrupted.compareAndSet(false, true)) {
this.custom.run(context.interruptAs(context.id));
}
})(signal);
};
process.once('SIGTERM', handler);
process.once('SIGINT', handler);
}
}
const nodeRuntime = new NodeRuntime(
defaultRuntime.traceRenderer({
renderTrace: N.nodeTracer,
renderError: Cause.defaultRenderer.renderError,
renderUnknown: Cause.defaultRenderer.renderUnknown,
}),
);
/** An alternative runMain that prints JSON instead of raw text. */
export const runMain = nodeRuntime.runMain;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment