UNPKG

loop-controls

Version:

break/continue controls for loops and higher-order functions (sync, async, concurrent). Designed to compose with ts-pattern.

83 lines (76 loc) 2.4 kB
import { _Break as BaseBreak, _Continue as BaseContinue, type Control } from "./core.js"; export function forEach<T>( iter: Iterable<T>, fn: (item: T, control: Control, index: number) => void ): { broken: false } | { broken: true } { class LoopBreak<T = unknown> extends BaseBreak<T> {} class LoopContinue extends BaseContinue {} const $: Control = { break: (value?: unknown): never => { throw new LoopBreak(value); }, continue: (): never => { throw new LoopContinue(); }, }; let i = 0; for (const item of iter) { try { fn(item, $, i++); } catch (e) { if (e instanceof LoopContinue) continue; if (e instanceof LoopBreak) return { broken: true }; throw e; } } return { broken: false }; } export function reduce<T, A>( iter: Iterable<T>, seed: A, fn: (acc: A, item: T, control: Control, index: number) => A ): A { class LoopBreak<T = unknown> extends BaseBreak<T> {} class LoopContinue extends BaseContinue {} const $: Control = { break: (value?: unknown): never => { throw new LoopBreak(value); }, continue: (): never => { throw new LoopContinue(); }, }; let acc = seed, i = 0; for (const item of iter) { try { acc = fn(acc, item, $, i++); } catch (e) { if (e instanceof LoopContinue) continue; if (e instanceof LoopBreak) return ((e as LoopBreak).value !== undefined ? (e as LoopBreak).value : acc) as A; throw e; } } return acc; } export function find<T>( iter: Iterable<T>, fn: (item: T, control: Control, index: number) => boolean ): T | undefined; export function find<T, S extends T>( iter: Iterable<T>, fn: (item: T, control: Control, index: number) => item is S ): S | undefined; export function find<T>( iter: Iterable<T>, fn: (item: T, control: Control, index: number) => boolean ): T | undefined { class LoopBreak<T = unknown> extends BaseBreak<T> {} class LoopContinue extends BaseContinue {} const $: Control = { break: (value?: unknown): never => { throw new LoopBreak(value); }, continue: (): never => { throw new LoopContinue(); }, }; let i = 0; for (const item of iter) { try { if (fn(item, $, i++)) return item; } catch (e) { if (e instanceof LoopContinue) continue; if (e instanceof LoopBreak) return (e as LoopBreak).value as T | undefined; throw e; } } return undefined; }