import { assert, assertEquals, assertInstanceOf } from "https://deno.land/std@0.221.0/assert/mod.ts"; import * as path from "https://deno.land/std@0.221.0/path/mod.ts"; import { TextLineStream } from "https://deno.land/std@0.221.0/streams/mod.ts"; import { z, ZodType } from "https://deno.land/x/zod@v3.22.4/mod.ts"; export function parsed_stream(parser: ZodType) { return async function* (readable: ReadableStream) { const line_stream = readable .pipeThrough(new TextDecoderStream()) .pipeThrough(new TextLineStream()); for await (const line of line_stream) yield parser.parse(JSON.parse(line)); }; } export function serialized_stream(generator: AsyncIterable) { return async function (writable: WritableStream) { const stream = new TextEncoderStream(); stream.readable.pipeTo(writable); for await (const item of generator) { const writer = stream.writable.getWriter(); await writer.write(JSON.stringify(item) + "\n"); writer.releaseLock(); } }; } export type Consumer = (item: T) => void; // deno-lint-ignore no-explicit-any export type ParserOutput ZodType> = z.infer>; export type PromiseSplit = ReturnType>; export function split_promise() { let resolver_ = null as Consumer | null; const promise = new Promise((r) => { resolver_ = r; }); assert(resolver_ !== null); const resolver = resolver_ as Consumer; return { promise, resolver }; } export class AsyncQueue { private items; private awaiting; public constructor() { this.items = [] as T[]; this.awaiting = [] as Consumer[]; } public push(item: T) { this.items.push(item); const [first] = this.awaiting.splice(0, 1); if (first !== undefined) first(); } public async pull() { let first = this.take_first_item(); if (first !== undefined) return first; const { promise, resolver } = split_promise(); this.awaiting.push(resolver); await promise; first = this.take_first_item(); assert(first !== undefined); return first as T; } private take_first_item() { const [first] = this.items.splice(0, 1); return first as T | undefined; } } export function channel() { const queue = new AsyncQueue(); return [new Sender(queue), new Receiver(queue)] as const; } export function channel_generator(generator: AsyncIterable) { const [sender, receiver] = channel(); (async () => { for await (const item of generator) sender.send(item); })(); return receiver; } export class Sender { queue; public constructor(queue: AsyncQueue) { this.queue = queue; } public send(item: T) { this.queue.push(item); } public async send_all(iter: AsyncGenerator) { for await (const item of iter) this.send(item); } } export class Receiver { queue; public constructor(queue: AsyncQueue) { this.queue = queue; } public async receive() { return await this.queue.pull(); } public async *iter() { while (true) yield this.receive(); } } export function range(from: number, to: number) { return new Range(from, to); } export class Range { from; to; constructor(from: number, to: number) { this.from = from; this.to = to; } includes(n: number) { return (n >= this.from) && (n < this.to); } [Symbol.iterator]() { let index = this.from; const to = this.to; return (function* () { while (index < to) yield index++; })(); } } export async function launch_caught( operation: () => Promise | T, handler: null | ((error: unknown) => void) = null, ) { try { return await operation(); } catch (error) { if (handler !== null) handler(error); else console.error("Lanched Uncaught :", error); } } export function log_from(meta: ImportMeta) { return function (...args: unknown[]) { const mod = path.basename(new URL(meta.url).pathname); console.log(`[${mod}]`, ...args); }; } export class Vec2 { private x_; private y_; public constructor(x: number, y: number) { this.x_ = Math.round(x); this.y_ = Math.round(y); } public x() { return this.x_; } public y() { return this.y_; } public scale(factor: number) { return new Vec2(this.x_ * factor, this.y_ * factor); } public neg() { return this.scale(-1); } public add(other: Vec2) { return new Vec2(this.x_ + other.x_, this.y_ + other.y_); } public sub(other: Vec2) { return this.add(other.neg()); } public inside(corner_tl: Vec2, corner_br: Vec2) { if (!range(corner_tl.x(), corner_br.x()).includes(this.x())) return false; if (!range(corner_tl.y(), corner_br.y()).includes(this.y())) return false; return true; } public eq(other: Vec2) { return (this.x() === other.x()) && (this.y() === other.y()); } public neq(other: Vec2) { return !this.eq(other); } public len() { return Math.sqrt(this.x() ** 2 + this.y() ** 2); } public distance(other: Vec2) { return this.sub(other).len(); } public overlaps(other: Vec2) { return this.distance(other) < 1; } } export function v2(x: number, y: number) { return new Vec2(x, y); } export function* enumerate(iterator: Iterable) { let index = 0; for (const item of iterator) yield [index++, item] as [number, T]; } export function* chunks(iterator: Iterable, size: number) { let current = []; for (const item of iterator) { current.push(item); if (current.length < size) continue; yield current; current = []; } } Deno.test("test_chunk", () => { assertEquals( [...chunks([1, 2, 3, 4, 5, 6, 7], 3)], [[1, 2, 3], [4, 5, 6]], ); }); export function wait(ms: number) { return new Promise((res) => setTimeout(res, ms)); } export async function run(cmd: string, ...args: string[]) { const command = new Deno.Command(cmd, { args, stdout: "piped" }); const output = await command.output(); if (!output.success) throw new Error(); return new TextDecoder().decode(output.stdout); } export type Prototyped

= { constructor: { prototype: P } }; export type Constructible = new (...args: unknown[]) => I; export class ClassMap { private inner; constructor() { this.inner = new Map(); } insert(object: V) { const prototype = object.constructor.prototype; if (this.inner.has(prototype)) throw new Error(); this.inner.set(prototype, object); } get>(class_: C) { const value = this.inner.get(class_.prototype); if (value === undefined) return null; assertInstanceOf(value, class_); return value; } } Deno.test("test_classmap", () => { { class Truc {} class Machin {} class Chose {} const map = new ClassMap(); const truc = new Truc(); map.insert(truc); const machin = new Machin(); map.insert(machin); assert(map.get(Truc) === truc); assert(map.get(Machin) !== truc); assert(map.get(Machin) === machin); assert(map.get(Chose) === null); } { interface Machinable { machin(): number; } // deno-lint-ignore no-unused-vars class Truc {} class Machin implements Machinable { machin = () => 35; } const map = new ClassMap(); map.insert(new Machin()); // map.insert(new Truc()); // note : Fails with improper typing. map.get(Machin); // map.get(Truc); // note : Fails with improper typing. } }); export function maybe(item: T) { return item as T | undefined; }