packages feed

moonlight-triangulation-1.4.0.2: ffi/bindings/typescript/src/failure.ts

import type {
  AbiStatus,
  CoordinateError,
  ObstructionCode,
} from "./wire.generated.js";

import type { Point } from "./values.js";

export type Result<Value, Failure = MoonlightFailure> =
  | { readonly ok: true; readonly value: Value }
  | { readonly ok: false; readonly error: Failure };

export interface Obstruction {
  readonly status: AbiStatus;
  readonly code: ObstructionCode;
  readonly coordinateError: CoordinateError;
  readonly inputIndex: bigint;
  readonly firstIndex: bigint;
  readonly secondIndex: bigint;
  readonly firstValue: number;
  readonly secondValue: number;
  readonly point: Point;
  readonly message: string;
}

export type ResourceKind = "mesh" | "region" | "structuring-element";

export type MoonlightFailure =
  | {
      readonly kind: "native-library-unavailable";
      readonly path: string;
      readonly message: string;
    }
  | {
      readonly kind: "runtime-initialization-refused";
      readonly status: AbiStatus;
    }
  | {
      readonly kind: "abi-version-mismatch";
      readonly expected: number;
      readonly observed: number;
    }
  | {
      readonly kind: "abi-obstruction";
      readonly obstruction: Obstruction;
    }
  | {
      readonly kind: "disposed-resource";
      readonly resource: ResourceKind;
    }
  | {
      readonly kind: "foreign-runtime";
      readonly resource: ResourceKind;
    }
  | {
      readonly kind: "unsafe-integer";
      readonly value: bigint;
    }
  | {
      readonly kind: "unknown-wire-value";
      readonly vocabulary: "status" | "obstruction" | "coordinate-error" | "region-location" | "minkowski-operation";
      readonly value: number;
    }
  | {
      readonly kind: "invalid-native-result";
      readonly message: string;
    }
  | {
      readonly kind: "native-call-failed";
      readonly message: string;
    };

export function success<Value>(value: Value): Result<Value> {
  return { ok: true, value };
}

export function failure<Value = never>(error: MoonlightFailure): Result<Value> {
  return { ok: false, error };
}

export function collectResults<Value>(results: readonly Result<Value>[]): Result<readonly Value[]> {
  const refused = results.find((result) => !result.ok);
  return refused?.ok === false
    ? failure(refused.error)
    : success(results.flatMap((result) => (result.ok ? [result.value] : [])));
}

export function formatMoonlightFailure(error: MoonlightFailure): string {
  switch (error.kind) {
    case "native-library-unavailable":
      return `cannot load Moonlight native library at ${error.path}: ${error.message}`;
    case "runtime-initialization-refused":
      return `Moonlight runtime initialization refused with ${error.status}`;
    case "abi-version-mismatch":
      return `Moonlight ABI ${error.observed} is incompatible with expected ABI ${error.expected}`;
    case "abi-obstruction":
      return error.obstruction.message || `${error.obstruction.status}: ${error.obstruction.code}`;
    case "disposed-resource":
      return `${error.resource} is disposed`;
    case "foreign-runtime":
      return `${error.resource} belongs to another Moonlight runtime`;
    case "unsafe-integer":
      return `Moonlight count ${error.value} exceeds JavaScript's safe integer range`;
    case "unknown-wire-value":
      return `Moonlight returned unknown ${error.vocabulary} value ${error.value}`;
    case "invalid-native-result":
      return error.message;
    case "native-call-failed":
      return error.message;
  }
}