enso/app/gui2/shared/util/data/result.ts
Adam Obuchowicz a6fc8cb932
Reimplement AI PoC from GUI1 (#9476)
Fixes #9313

[Screencast from 2024-03-22 09-09-07.webm](https://github.com/enso-org/enso/assets/3919101/6ad86145-6882-4bde-993d-b1270f1ec06c)

# Important Notes
* This is PoC, so I didn't spend time on polishing the visuals; the design will likely change.
* I modified the shortcut handler a bit, allowing making multiple actions for same binding - the action's handler will be called in unspecified order, until one of them handle the event (i.e. not return false).
* To make it working regardless of imports, I needed to export AI module in Standard.Visualization. Moreover, needed to remove build_ai_prompt for Any, because it was causing issues - expect a bug report soon.
2024-03-25 09:14:41 +00:00

90 lines
2.4 KiB
TypeScript

/** @file A generic type that can either hold a value representing a successful result,
* or an error. */
import { isSome, type Opt } from './opt'
export type Result<T = undefined, E = string> =
| { ok: true; value: T }
| { ok: false; error: ResultError<E> }
export function Ok<T>(data: T): Result<T, never> {
return { ok: true, value: data }
}
export function Err<E>(error: E): Result<never, E> {
return { ok: false, error: new ResultError(error) }
}
export function okOr<T, E>(data: Opt<T>, error: E): Result<T, E> {
if (isSome(data)) return Ok(data)
else return Err(error)
}
export function unwrap<T, E>(result: Result<T, E>): T {
if (result.ok) return result.value
else throw result.error
}
export function mapOk<T, U, E>(result: Result<T, E>, f: (value: T) => U): Result<U, E> {
if (result.ok) return Ok(f(result.value))
else return result
}
export function isResult(v: unknown): v is Result {
return (
v != null &&
typeof v === 'object' &&
'ok' in v &&
typeof v.ok === 'boolean' &&
('value' in v || ('error' in v && v.error instanceof ResultError))
)
}
export class ResultError<E = string> {
payload: E
context: (() => string)[]
constructor(payload: E) {
this.payload = payload
this.context = []
}
log(preamble: string = 'Error') {
console.error(this.message(preamble))
}
message(preamble: string = 'error') {
const ctx =
this.context.length > 0 ? `\n${Array.from(this.context, (ctx) => ctx()).join('\n')}` : ''
return `${preamble}: ${this.payload}${ctx}`
}
}
export function withContext<T, E>(context: () => string, f: () => Result<T, E>): Result<T, E> {
const result = f()
if (result == null) {
throw new Error('withContext: f() returned null or undefined')
}
if (!result.ok) result.error.context.splice(0, 0, context)
return result
}
/**
* Catch promise rejection of provided types and convert them to a Result type.
*/
export function rejectionToResult<ErrorKind extends new (...args: any[]) => any>(
errorKinds: ErrorKind | ErrorKind[],
): <T>(promise: Promise<T>) => Promise<Result<T, InstanceType<ErrorKind>>> {
const errorKindArray = Array.isArray(errorKinds) ? errorKinds : [errorKinds]
return async (promise) => {
try {
return Ok(await promise)
} catch (error) {
for (const errorKind of errorKindArray) {
if (error instanceof errorKind) return Err(error)
}
throw error
}
}
}