rust-result-js
rust Result object implemented in javascript.
Table of Contents
Installation
via npm⚡:
$ npm i rust-result-js
or yarn⚡:
$ yarn add rust-result-js
or pnpm⚡:
$ pnpm add rust-result-js
Usage
This package contains 2 implementations- with createResult
and without. Its error handling is identical to rust's:
expect, unwrap, map...
However, since Javascript's convention is not snake-case, some methods differ by their name - all those with snake case were converted to camelCase.
examples:
- map_err =
mapErr
in js - is_ok =
isOk
in js - unwrap_err =
unwrapErr
in js - etc...
Example
Unlike rust, in javascript, in order to return a value you must use the return
keyword.
import { Ok, Err } from 'rust-result-js';
const toNumber = (str) => {
const parsed = +str;
if (!parsed && parsed !== 0) return Err("could not parse");
return Ok(parsed);
}
const six = toNumber("6").expect("failed parsing 6");
console.log(`formatted number is ${six}`);
// throws error:
// panics with expect() - failed parsing someInput : could not parse
const someInput = toNumber("not a number").expect("failed parsing someInput");
createResult
Using For those who HATE using return
:
import { createResult } from 'rust-result-js';
const toNumber = str => createResult((ok, err) => {
const parsed = +str;
if (!parsed && parsed !== 0) err("could not parse");
ok(parsed);
})
const four = toNumber("4").expect("failed parsing 4");
console.log(`formatted number is ${four}`);
This way it looks a lot more like the original rust
syntax, but overall, it seems less aesthetic.
Note: when using createResult
, you shouldn't use return
with ok
and err
.
Typescript
This package also supports typescript, as it introduces the Result
abstract class
import {Ok, Err, Result} from 'rust-result-js';
const fetchSomething = async (): Result<string[], string> => {
try {
const todosFetch = await fetch('http://something:1234/todos');
const todos: string[] = await todosFetch.json();
return Ok(todos);
} catch (e) {
return Err("error fetching todos");
}
}
Available functions
NOTE: available functions
API is identical to rust's original implementation.
This is what implemented so far in this library:
isOk
rust docsisErr
rust docsexpect
rust docsexpectErr
rust docsunwrap
rust docsunwrapErr
rust docsmap
rust docsmapErr
rust docsmapOr
rust docscontains
rust docscontainsErr
rust docs- And more to come...
API References
isOk
function isOk(): boolean{/*...*/}
returns true
if the Result is Ok
, else returns false
.
const x = Ok(5);
const y = Err('oops! something went wrong');
console.log(x.isOk()) // true
console.log(y.isOk()) // false
isErr
function isErr(): boolean{/*...*/}
Opposite of isOk
, returns true
if Result is Err
, else returns false
const x = Ok('im Ok!');
const y = Err('oops! something went wrong');
console.log(x.isErr()) // false
console.log(y.isErr()) // true
expect
function expect(msg: string): T{/*...*/}
where T - type of Ok
value
Returns the contained Ok
value. If Err
, throws error with msg
including the errorValue.
const x = Err("emergency failure");
const val = x.expect("testing expect");
//output: panics with expect() - testing expect : emergency failure.
const x = Ok(6).expect("error with x"); // x = 6
console.log(x) //output: 6
expectErr
function expectErr(msg: string): K{/*...*/}
where K - type of Err
value
Returns the contained Err
value. If Ok
, throws error with msg
including Ok's value
const x = Ok(6).expectErr("error with x");
//output: [Error] panics with expectErr() : error with x : 6
const x = Err("my error value").expectErr("testing expect");
console.log(x) // output: "my error value"
unwrap
function unwrap(): T{/*...*/}
where T
type = Ok value's type
Returns the contained Ok
value, throws if the value is an Err
.
const x = Err(3);
x.unwrap();
// this will throw an error
const x = Ok(3);
console.log(x.unwrap())
//output: 3
unwrapErr
function unwrapErr(): K{/*...*/}
where K
is Err value's Type
Returns the contained Err
value, throws if the value is an Ok
.
map
function map<T1>(fn: (val: T) => T1): Result<T1, K>{/*...*/}
Maps a Result<T, E>
to Result<U, E>
by applying a function to a contained Ok
value, leaving an Err
value
untouched.
const three = Ok('3');
const threeAsNumber = three.map(val => parseInt(val));
console.log(typeof val.unwrap());
//output: number
mapErr
function mapErr<K1>(fn: (err: K) => K1): Result<T, K1>{/*...*/}
Maps a Result<T, E>
to Result<T, F>
by applying a function to a contained Err
value, leaving an Ok
value
untouched.
const errMsg = Err('this is just a message')
const decoratedError = errMsg.mapErr(val => `[ERROR]: ${val}`);
console.log(decoratedError.unwrapErr());
//output: [Error]: this is just a message
mapOr
function mapOr<T1>(defaultValue: T1, fn: (val: T) => T1): T1{/*...*/}
where T1 new Ok
value's type
Returns the provided default (if Err
), or applies a function to the contained value (if Ok
).
const weirdFetch = async () => {
await sleep(1000);
const shouldCrash = Math.random() < .5;
if (shouldCrash) {
return Err(null)
}
return Ok({
name: 'Deez Nuts',
id: '666666666'
});
}
const fetchedPerson = await weirdFetch();
const person = fetchedPerson.mapOr(
{lname: '', fname: '', id: '0'},
person => {
const [fname, ...lname] = person.name.split(' ');
return {
id: person.id,
fname,
lname: lname.join(' '),
}
}
)
console.log(person)
//output:
// in case of Err:
// {lname: '', fname: '', id: '0'}
// in case of Ok:
// {lname: 'Nuts', fname: 'Deez', id: '6666666666'}
contains
function contains(value: T): boolean{/*...*/}
Returns true
if the result is an Ok
value containing the given value.
NOTE: It uses Lodash.isEqual
to compare deep between objects.
const x = Ok({a: {b: 3}});
console.log(x.contains({a: {b: 3}})) // returns true
containsErr
function containsErr(errValue: K): boolean{/*...*/}
Returns true if the result is an Err value containing the given value.
NOTE: It uses Lodash.isEqual
to compare deep between objects.
const x = Err({a: {b: 3}});
console.log(x.containsErr({a: {b: 3}})) // returns true
Contributions
First, if you read this far you deserve congratulations.
Every contributor is welcome. There is a need to keep implementing more functions provided by rust
's Result Object (at
least those which we can implement).
Code Structure
The Ok
and Err
are basically wrappers for 2 classes, both of which are inheriting from an abstract
class Result<T,K>
.
Should you choose to add more functionality, please do the following:
- Add abstract function to
Result<T,K>
abstract class Result<T, K> {
abstract myFucntion()
}
- Implement it in both
OkResult
andErrResult
classes respectively.
Branch Convention
For writing features - please branch out from dev
under feature/
(e.g. feature/contains_or
)
For fixing bugs - please branch out from dev
under bugfix/
(e.g. bugfix/create-result-for-async
)