init
This commit is contained in:
commit
6694c7eb9a
10 changed files with 328 additions and 0 deletions
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Barnulf Ts
|
||||
|
||||
A collection of utility functions and classes extending base JavaScript.
|
||||
|
||||
Intended for the Deno runtime.
|
6
deno.json
Normal file
6
deno.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fmt": {
|
||||
"lineWidth": 120,
|
||||
"useTabs": true
|
||||
}
|
||||
}
|
38
deno.lock
generated
Normal file
38
deno.lock
generated
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"version": "3",
|
||||
"remote": {
|
||||
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
|
||||
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
|
||||
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
|
||||
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
|
||||
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
|
||||
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
|
||||
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
|
||||
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
|
||||
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
|
||||
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
|
||||
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
|
||||
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
|
||||
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
|
||||
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
|
||||
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
|
||||
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
|
||||
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
|
||||
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
|
||||
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
|
||||
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
|
||||
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
|
||||
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
|
||||
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
|
||||
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
|
||||
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
|
||||
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
|
||||
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
|
||||
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
|
||||
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e"
|
||||
}
|
||||
}
|
1
mod.ts
Normal file
1
mod.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./src/lib.ts";
|
10
src/lib.ts
Normal file
10
src/lib.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
// types
|
||||
export type { Arr, ClassOf, Constructible, Function, InstanceOf, KeyOfType, Tail } from "./lib/types.ts";
|
||||
|
||||
// functions
|
||||
export { all, enumerate, filter, filter_map, it, Iter, map, zip } from "./lib/iter.ts";
|
||||
export { next, range, split_promise, wait } from "./lib/utils.ts";
|
||||
|
||||
// Structures
|
||||
export { Chain } from "./lib/Chain.ts";
|
||||
export { Channel } from "./lib/Channel.ts";
|
113
src/lib/Chain.ts
Normal file
113
src/lib/Chain.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { Arr, Function, KeyOfType, Tail } from "./types.ts";
|
||||
import { map } from "./iter.ts";
|
||||
|
||||
export class Chain<T extends Arr> {
|
||||
run;
|
||||
|
||||
constructor(run: () => T) {
|
||||
this.run = run;
|
||||
}
|
||||
|
||||
static from<T>(value: T) {
|
||||
return new Chain(() => [value] as [T]);
|
||||
}
|
||||
|
||||
to<A extends Arr, O>(map: Function<[...T, ...A], O>, ...args: A) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => [map(...this_run(), ...args)] as [O]);
|
||||
}
|
||||
|
||||
front<I extends Arr>(...values: I) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => [...values, ...this_run()] as [...I, ...T]);
|
||||
}
|
||||
|
||||
back<I extends Arr>(...values: I) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => [...this_run(), ...values] as [...T, ...I]);
|
||||
}
|
||||
|
||||
nth<Index extends number>(index: Index) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => [this_run()[index]] as T[Index]);
|
||||
}
|
||||
|
||||
call<
|
||||
A extends Arr,
|
||||
Arg extends [...Tail<T>, ...A],
|
||||
M extends KeyOfType<T[0], Function<Arg>>,
|
||||
O extends ReturnType<T[0][M]>,
|
||||
>(method: M, ...args: A) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => {
|
||||
const [instance, ...rest] = this_run();
|
||||
return [instance[method](...rest, ...args)] as [O];
|
||||
});
|
||||
}
|
||||
|
||||
call_ignore<
|
||||
A extends Arr,
|
||||
Arg extends [...Tail<T>, ...A],
|
||||
M extends KeyOfType<T[0], Function<Arg>>,
|
||||
>(method: M, ...args: A) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => {
|
||||
const res1: T = this_run();
|
||||
const [instance, ...rest] = res1;
|
||||
instance[method](...rest, ...args);
|
||||
return res1;
|
||||
});
|
||||
}
|
||||
|
||||
call_keep<
|
||||
A extends Arr,
|
||||
Arg extends [...Tail<T>, ...A],
|
||||
M extends KeyOfType<T[0], Function<Arg>>,
|
||||
O extends ReturnType<T[0][M]>,
|
||||
>(method: M, ...args: A) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => {
|
||||
const res1 = this_run();
|
||||
const [instance, ...rest] = res1;
|
||||
const res2: O = instance[method](...rest, ...args);
|
||||
return [...res1, res2] as [...T, O];
|
||||
});
|
||||
}
|
||||
|
||||
construct<
|
||||
A extends Arr,
|
||||
C extends new (...args: [...T, ...A]) => unknown,
|
||||
O extends InstanceType<C>,
|
||||
>(class_: C, ...args: A) {
|
||||
const this_run = this.run;
|
||||
return new Chain(() => [new class_(...this_run(), ...args)] as [O]);
|
||||
}
|
||||
}
|
||||
Deno.test("example", async () => {
|
||||
const { assertEquals } = await import("https://deno.land/std@0.224.0/assert/mod.ts");
|
||||
|
||||
function times_2(num: number) {
|
||||
return num * 2;
|
||||
}
|
||||
|
||||
// type: number
|
||||
const [_o1] = Chain.from(3).to(times_2).run();
|
||||
// type: number
|
||||
const [_o2] = Chain.from(3).front(2, 5).to(Math.max).run();
|
||||
|
||||
function parse_and_add(text: string, num: number) {
|
||||
return parseInt(text) + num;
|
||||
}
|
||||
|
||||
// type: string
|
||||
const [_o3] = Chain.from(3).front("5").to(parse_and_add).call("toString").run();
|
||||
|
||||
// type: Set<number>
|
||||
const [_o4] = Chain.from([1, 2, 3])
|
||||
.call_keep("push", 7)
|
||||
.construct(Set<number>)
|
||||
.call_ignore("add", 8)
|
||||
.to(map, (item: number) => item.toString())
|
||||
.run();
|
||||
assertEquals([..._o4], ["1", "2", "3", "7", "8"]);
|
||||
});
|
23
src/lib/Channel.ts
Normal file
23
src/lib/Channel.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { split_promise } from "./utils.ts";
|
||||
|
||||
export class Channel<T> {
|
||||
queue_receiving = [] as ((item: T) => void)[];
|
||||
queue_sending = [] as (() => T)[];
|
||||
|
||||
async send(item: T) {
|
||||
const [first] = this.queue_receiving.splice(0, 1);
|
||||
if (first !== undefined) return first(item);
|
||||
const [promise, resolve] = split_promise();
|
||||
const fn = () => ([resolve(), item] as const)[1];
|
||||
this.queue_sending.push(fn);
|
||||
await promise;
|
||||
}
|
||||
|
||||
async receive() {
|
||||
const [first] = this.queue_sending.splice(0, 1);
|
||||
if (first !== undefined) return first();
|
||||
const [promise, resolve] = split_promise<T>();
|
||||
this.queue_receiving.push(resolve);
|
||||
return await promise;
|
||||
}
|
||||
}
|
97
src/lib/iter.ts
Normal file
97
src/lib/iter.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { next, range } from "./utils.ts";
|
||||
|
||||
export function it<T>(iter: Iterable<T>) {
|
||||
return new Iter(iter);
|
||||
}
|
||||
|
||||
export function* map<I, O>(iter: Iterable<I>, mapping: (item: I) => O): Generator<O, void, void> {
|
||||
for (const item of iter) yield mapping(item);
|
||||
}
|
||||
|
||||
export function* filter<I>(iter: Iterable<I>, predicate: (item: I) => boolean): Generator<I, void, void> {
|
||||
for (const item of iter) if (predicate(item)) yield item;
|
||||
}
|
||||
|
||||
export function filter_map<I, O>(
|
||||
iter: Iterable<I>,
|
||||
mapping: (item: I) => O,
|
||||
): Generator<Exclude<O, null | undefined>, void, void> {
|
||||
const filtered = filter(map(iter, mapping), (item) => item !== null && item !== undefined);
|
||||
return filtered as Generator<Exclude<O, null | undefined>, void, void>;
|
||||
}
|
||||
|
||||
export function all<I>(iter: Iterable<I>, predicate: (item: I) => boolean) {
|
||||
for (const item of iter) if (!predicate(item)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function* enumerate<T>(iterator: Iterable<T>): Generator<[number, T], void, void> {
|
||||
let index = 0;
|
||||
for (const item of iterator) yield [index++, item];
|
||||
}
|
||||
|
||||
export function* zip<A, B>(a: Iterable<A>, b: Iterable<B>): Generator<[A, B], void, void> {
|
||||
const iter_a = a[Symbol.iterator](), iter_b = b[Symbol.iterator]();
|
||||
while (true) {
|
||||
const next_a = next(iter_a), next_b = next(iter_b);
|
||||
if (next_a === null || next_b === null) return;
|
||||
yield [next_a, next_b];
|
||||
}
|
||||
}
|
||||
|
||||
export class Iter<T> {
|
||||
iter;
|
||||
[Symbol.iterator] = () => this.iter[Symbol.iterator]();
|
||||
|
||||
constructor(iter: Iterable<T>) {
|
||||
this.iter = iter;
|
||||
}
|
||||
|
||||
map<O>(mapping: (item: T) => O) {
|
||||
return new Iter(map(this, mapping));
|
||||
}
|
||||
|
||||
filter(predicate: (item: T) => boolean) {
|
||||
return new Iter(filter(this, predicate));
|
||||
}
|
||||
|
||||
filter_map<O>(mapping: (item: T) => O) {
|
||||
return new Iter(filter_map(this, mapping));
|
||||
}
|
||||
|
||||
enumerate() {
|
||||
return new Iter(enumerate(this));
|
||||
}
|
||||
|
||||
zip<U>(other: Iterable<U>) {
|
||||
return new Iter(zip(this, other));
|
||||
}
|
||||
|
||||
array() {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const result: any = [...this];
|
||||
result.it = () => it(result);
|
||||
return result as T[] & { it: () => Iter<T> };
|
||||
}
|
||||
|
||||
collect<O>(class_: new (it: Iterable<T>) => O): O {
|
||||
return new class_(this);
|
||||
}
|
||||
|
||||
all(predicate: (item: T) => boolean) {
|
||||
return all(this, predicate);
|
||||
}
|
||||
}
|
||||
|
||||
Deno.test("iter", async () => {
|
||||
const items = it(range(0, 10))
|
||||
.map((e) => e * 3)
|
||||
.enumerate()
|
||||
.filter(([_, e]) => (e % 2) == 1)
|
||||
.zip(range(0, 10))
|
||||
.map(JSON.stringify)
|
||||
.collect(Set<string>);
|
||||
|
||||
const { assertEquals } = await import("https://deno.land/std@0.224.0/assert/assert_equals.ts");
|
||||
assertEquals(items, new Set(["[[1,3],0]", "[[3,9],1]", "[[5,15],2]", "[[7,21],3]", "[[9,27],4]"]));
|
||||
});
|
12
src/lib/types.ts
Normal file
12
src/lib/types.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
// deno-lint-ignore no-explicit-any
|
||||
export type Arr<T = any> = T[];
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export type Function<I extends Arr, O = any> = (...input: I) => O;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export type Tail<A extends Arr> = A extends [any, ...infer T] ? T : never;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export type KeyOfType<T, V> = keyof { [P in keyof T as T[P] extends V ? P : never]: any };
|
||||
|
||||
export type InstanceOf<C = unknown> = { constructor: { prototype: C } };
|
||||
export type Constructible<I, Args extends Arr> = new (...args: Args) => I;
|
||||
export type ClassOf<I = unknown> = Constructible<I, unknown[]>;
|
23
src/lib/utils.ts
Normal file
23
src/lib/utils.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { Function } from "./types.ts";
|
||||
|
||||
export function split_promise<T = void>() {
|
||||
let resolver_or_null = null as null | Function<[T], void>;
|
||||
const promise = new Promise<T>((res) => resolver_or_null = res);
|
||||
if (resolver_or_null === null) throw new Error("Unreachable.");
|
||||
const resolver = resolver_or_null;
|
||||
return [promise, resolver] as const;
|
||||
}
|
||||
|
||||
export function wait(ms: number) {
|
||||
return new Promise((res) => setTimeout(res, ms));
|
||||
}
|
||||
|
||||
export function next<T>(iterator: Iterator<T>) {
|
||||
const result = iterator.next().value;
|
||||
if (result === undefined) return null;
|
||||
else return result as T;
|
||||
}
|
||||
|
||||
export function* range(from: number, to: number) {
|
||||
while (from < to) yield from++;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue