npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

derive-rust

v1.0.1

Published

Rust-like utils for JavaScript. Start using match pattern, enums with generics and other features similarly to the Rust programming language.

Downloads

94

Readme

Rust-like utils for JavaScript

Start using match pattern, enums with generics and other features similarly to the Rust programming language.

Option<T>

const a = Some("hello");
const b: Option<string> = None();
const c = None<string>();

a.match({
  None: () => "",
  Some: (v) => v + " world!"
})

c.match({
  None: () => "",
  Some: (v) => v
})

// Data validation

const opt = Option.from<number>(undefined);

const x2Num = opt.match({
  Some: (num) => num * 2,
  None: () => panic("null or undefined")
});

Result<T, E>

const fooOk: Result<{result: boolean}, {}> = Ok({result: true});
const fooErr: Result<{result: boolean}, {}> = Err({});

// Data validation

const result = Result.from<{data: Data[]}>(null);

const data = result.match({
  Err: () => panic("null or undefined"),
  Ok: (value) => value.data
});

// Error interception
Result.fromNever(() => { throw "Thrown" }).unwrapErr() // Thrown

Result.fromPromise(Promise.reject("Rejected"))
  .then(r => r.unwrapErr()) // Rejected

Result.fromAsync(async () => await fetch("You should learn Rust"))
  .then(r => r.unwrapErr()) // Response error

Match

match("hello", [
  (hello) => [hello, () => hello + " world" ], // wrap arm in function for matching with `ref` value
  ["foo", "bar", () =>  /* anything */] // list many things for matching
], _ => "" ) // default expression

matches compare values. Returns boolean

matches(lhs, rhs, optCondition)

eqType compares types. Returns boolean


const o1 = {
    a: 1,
    barFn() {}
}

const o2 = {
    a: 1,
    fooFn() {}
}

eqType(o1, o2) // false. Compares all the properties

Box<T>

It can be useful for guarding values like empty strings or zeros which give you false result in comparisons. Also when you need to store null or undefined deliberately

[undefined, null, ...].find(value => value === undefined) // ???
// it returns undefined when nothing is found but array includes undefined as actual value

// Another example
function foo(): void { }

function bar(): void {
  return undefined;
}
// They are the same but what if...

function bar(): Box<undefined> {
  return new Box(undefined);
}

// or
// box function also added if you don't like `new` keyword
function bar(): Box<undefined> {
  return box(undefined);
}

const boxed = bar();
boxed.leak() // undefined as value

Structs

class Foo {
  #x: number;
  a: string;

  constructor(self: Self<Foo>) {
    // Assigns all the Self<S> properties except methods
    // Seals `this` by default with third argument
    // Be cautious. `this` - is the FIRST ARGUMENT
    impl(this, self);
  }

  f() {
    console.log(this.a)
  }
}

new Foo({
  a: "hello" // Self<S> checks only fields
})

Ranges

// 1..10
range(1, 10) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

// 1..=10
rangeInc(1, 10) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

// 'v'..='x'
rangeCharsInc('v', 'x', "stuvwxyz") // [ 'v', 'w', 'x' ]

// It works in the opposite way
range(5, -5) // [ 5, 4, 3, 2, 1, 0, -1, -2, -3, -4 ]
rangeInc(5, -5) // [ 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5 ]

// For reveresed result with strings you need to use rangeCharsRev() and rangeCharsRevInc() though
rangeCharsRev('b', 'f', "abcdefghI") // [ 'f', 'e', 'd', 'c' ]

// If omitted started/ended by empty string, you get tail/head respectively
rangeChars('c', '', "abcd12553") // ['c', 'd', '1', '2', '5', '5', '3']

// However it does work with distinctive characters
// You can see 5 two times so takes first one. If you need advanced approach then use string slice method
rangeChars('', '5', "abcd12553") // ['a', 'b', 'c','d', '1', '2', '5']

Channels

function syncChannel<T>(): [SyncSender<T>, SyncReceiver<T>];

const [tx,rx] = syncChannel();

tx.send(1) // returns Result<{}, SenderError> to check if valid value has been sent
tx.send(2)
rx.recv() // returns Option<T>. In this case Some(1)


// Async channel
const [tx, rx] = channel<{data: string[]}>();

tx.send(Promise.resolve({data: ["foo"]}));
tx.send(Promise.reject("ERROR"));
tx.send(Promise.resolve({data: ["bar"]}));

const last = 5

range(1, last).forEach(async (current) => {
  const result = await rx.recv();

  result.mapErr((err) => current !== 2 ? "Empty" : err).match({
    Ok:(data) => console.log("ok ==> ", data, current),
    Err:(err) => console.error("err ==> ", err, current)
  })
});

rx.recv().then(result => {
  result.mapErr(() => "Completed").match({
    Ok:(data) => console.log(data),
    Err:(err) => console.error("err ==>", err, last)
  })
}
);

// Output:

// ok ==>  { data: [ 'foo' ] } 1
// err ==>  ERROR 2
// ok ==>  { data: [ 'bar' ] } 3
// err ==>  Empty 4
// err ==> Completed 5

// Be aware `recv` has to be called in async scope

Declarations

function match<T, A>(value: T, arms: Array<Arm<T, A> | ArmFn<T, A>>, defaultArm: (value: T) => A): A;
function matches<T>(lhs: T, rhs: T, condition?: boolean): boolean;

type Self<S> = { [K in keyof S as S[K] extends Function ? never : K]: S[K] }

class Option<T> {
    #private;
    private constructor();
    static None<T>(): Option<T>;
    static Some<T>(value: T): Option<T>;
    static from<T>(value: T | null | undefined): Option<T>;
    match<A>(option: OptionArms<T, A>): A;
    unwrap(): T;
    unwrapOr(value: T): T;
    unwrapOrElse(f: () => T): T;
    expect(message: string): T;
    inspect(f: (value: T) => any): Option<T>;
    insert(value: T): T;
    getOrInsert(value: T): T;
    getOrInsertWith(f: () => T): T;
    isNone(): boolean;
    isSome(): boolean;
    isSomeAnd(f: (value: T) => boolean): boolean;
    intoResult<E>(error: E): Result<T, E>;
    map<F>(predicate: (value: T) => F): Option<F>;
    mapOr<V>(value: V, f: (value: T) => V): V;
    mapOrElse<V>(defaultF: () => V, f: (value: T) => V): V;
    filter(predicate: (value: T) => boolean): Option<T>;
    flatten(): Option<T>;
    take(): Option<T>;
    takeIf(predicate: (value: T) => boolean): Option<T>;
    replace(value: T): Option<T>;
    zip<U>(other: Option<U>): Option<[T, U]>;
    zipWith<O, R>(other: Option<O>, f: (self: T, other: O) => R): Option<R>;
    unzip<U>(): [Option<T>, Option<U>];
    transpose<E>(): Result<Option<T>, E>;
    okOr<E>(err: E): Result<T, E>;
    okOrElse<E>(err: () => E): Result<T, E>;
    and<U>(option: Option<U>): Option<U>;
    andThen<U>(f: (value: T) => Option<U>): Option<U>;
    or(option: Option<T>): Option<T>;
    orElse(f: () => Option<T>): Option<T>;
    xor(option: Option<T>): Option<T>;
    [Symbol.iterator](): Generator<T | undefined, void, unknown>;
    iter(): (T | undefined)[];
}

class Result<T, E> {
    #private;
    private constructor();
    static Err<E, T>(value: E): Result<T, E>;
    static Ok<T, E>(value: T): Result<T, E>;
    static from<T>(value: T | null | undefined): Result<T, null | undefined>;
    static fromNever<T, E>(fn: () => T): Result<T, E>;
    static fromPromise<T, E>(promise: Promise<T>): Promise<Result<T, E>>;
    static fromAsync<T, E>(fn: () => Promise<T>): Promise<Result<T, E>>;
    match<A>(arms: ResultArms<T, E, A>): A;
    isErr(): boolean;
    isErrAnd(f: (value: E) => boolean): boolean;
    isOk(): boolean;
    isOkAnd(f: (value: T) => boolean): boolean;
    ok(): Option<T>;
    err(): Option<E>;
    unwrap(): T;
    unwrapOr(value: T): T;
    unwrapOrElse(f: (err: E) => T): T;
    unwrapErr(): E;
    expect(message: string): T;
    intoOk(): T;
    intoErr(): E;
    intoOption(): Option<T>;
    map<F>(predicate: (ok: T) => F): Result<F, E>;
    mapOr<V>(value: V, f: (ok: T) => V): V;
    mapOrElse<F, V>(defaultF: (err: E) => V, f: (ok: T) => V): V;
    mapErr<F>(predicate: (err: E) => F): Result<T, F>;
    flatten(): Result<T, E>;
    transpose(): Option<Result<T, E>>;
    inspect(f: (ok: T) => any): Result<T, E>;
    inspectErr(f: (err: E) => any): Result<T, E>;
    and<U>(res: Result<U, E>): Result<U, E>;
    andThen<U>(f: (ok: T) => Result<U, E>): Result<U, E>;
    or<F>(res: Result<T, F>): Result<T, F>;
    orElse<F>(f: (err: E) => Result<T, F>): Result<T, F>;
    [Symbol.iterator](): Generator<T | E, void, unknown>;
    iter(): (T | E)[];
}

class Box<T> implements Sized<T> {
    #private;
    constructor(boxed: T);
    leak(): T;
}

box<T>(boxed: T): Box<T>;
function range(start: number, end: number): number[];
function rangeInc(start: number, end: number): number[];
function rangeChars(start: string, end: string, str: string): string[];
function rangeCharsInc(start: string, end: string, str: string): string[];
function rangeCharsRev(start: string, end: string, str: string): string[];
function rangeCharsRevInc(start: string, end: string, str: string): string[];
function clone<T>(value: T): T; // structuredClone but with methods
function syncChannel<T>(): [SyncSender<T>, SyncReceiver<T>];
function channel<T>(): [Sender<T>, Receiver<T>];
function impl<S>(target: S, self: S, seal: boolean): void;
function eqType<T>(lhs: T, rhs: T): boolean;
function eq<T>(lhs: T, rhs: T): 1 | -1 | 0;
function panic(reason: string): never;