Skip to content

Commit

Permalink
Add O.fromExecution and O.fromPromise
Browse files Browse the repository at this point in the history
  • Loading branch information
mobily committed Jul 30, 2022
1 parent 15cccdd commit 467d0e8
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 0 deletions.
49 changes: 49 additions & 0 deletions __tests__/Option/fromExecution.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { expectType } from 'ts-expect'
import { O } from '../..'

const okJSON = `{"name": "John"}`
const badJSON = `{name": John}`

type User = {
readonly name: string
}

const parseJSON =
<T>(value: string) =>
() => {
return JSON.parse(value) as T
}

describe('fromExecution', () => {
it('provides correct types', () => {
expectType<O.Option<User>>(O.fromExecution(parseJSON<User>(okJSON)))
})

it('returns None', () => {
expect(O.fromExecution(parseJSON(badJSON))).toEqual(O.None)
})

it('returns Some', () => {
expect(O.fromExecution(parseJSON(okJSON))).toEqual(
O.Some({
name: 'John',
}),
)
})

it('*', () => {
const { Some, None } = O

expect(
/*
type User = { readonly name: string }
const parseJSON = <T>(value: string) => () => JSON.parse(value) as T
const okJSON = `{"name": "John"}`
const badJSON = `{name": John}`
*/
O.fromExecution(parseJSON<User>(okJSON)),
).toEqual(Some({ name: 'John' }))

expect(O.fromExecution(parseJSON<User>(badJSON))).toEqual(None)
})
})
28 changes: 28 additions & 0 deletions __tests__/Option/fromPromise.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expectType } from 'ts-expect'
import { O } from '../..'

describe('fromPromise', () => {
it('provides correct types', () => {
expectType<Promise<O.Option<number>>>(O.fromPromise(Promise.resolve(42)))
})

it('returns None', async () => {
expect(await O.fromPromise(Promise.reject('hello world'))).toEqual(O.None)
})

it('returns Some', async () => {
expect(await O.fromPromise(Promise.resolve('hello world'))).toEqual(
O.Some('hello world'),
)
})

it('*', async () => {
const { Some, None } = O

expect(await O.fromPromise(Promise.resolve('hello world'))).toEqual(
Some('hello world'),
)

expect(await O.fromPromise(Promise.reject('oops'))).toEqual(None)
})
})
19 changes: 19 additions & 0 deletions src/Option/Option.res
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ let fromFalsy = value => value ? Some(value) : None
let fromPredicate = (value, predicateFn) =>
value->fromNullable->Belt.Option.flatMap(value => predicateFn(value) ? Some(value) : None)

%comment(
"Returns `Some(value)` (`value` is the result of `fn`) if `fn` didn't throw an error, otherwise, returns `None`."
)
@gentype
let fromExecution = fn => {
try {
Some(fn())
} catch {
| _ => None
}
}

%comment("Returns `Some(value)` if `promise` is resolved successfully, otherwise, returns `None`.")
@gentype
let fromPromise = promise => {
open Js.Promise
promise->then_(value => resolve(Some(value)), _)->catch(_ => resolve(None), _)
}

%comment(
"Returns the result of `mapFn` if `option` is `Some(value)`, otherwise, returns `None` and `mapFn` is not called."
)
Expand Down
4 changes: 4 additions & 0 deletions src/Option/Option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export declare function fromPredicate<A>(
value: A,
predicateFn: (value: NonNullable<A>) => boolean,
): Option<ExtractValue<A>>
export declare function fromExecution<A>(fn: () => A): Option<ExtractValue<A>>
export declare function fromPromise<A>(
promise: Promise<A>,
): Promise<Option<ExtractValue<A>>>
export declare function filter<A>(
option: Option<A>,
predicateFn: (value: A) => boolean,
Expand Down
10 changes: 10 additions & 0 deletions src/Option/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ export declare function fromPredicate<A>(
predicateFn: (value: NonNullable<A>) => boolean,
): (value: A) => Option<ExtractValue<A>>

/** Returns `Some(value)` (`value` is the result of `fn`) if `fn` didn't throw an error, otherwise, returns `None`. */

export declare function fromExecution<A>(fn: () => A): Option<ExtractValue<A>>

/** Returns `Some(value)` if `promise` is resolved successfully, otherwise, returns `None`. */

export declare function fromPromise<A>(
promise: Promise<A>,
): Promise<Option<ExtractValue<A>>>

/** Returns the result of `mapFn` if `option` is `Some(value)`, otherwise, returns `None` and `mapFn` is not called. */

export declare function map<A, B>(
Expand Down

0 comments on commit 467d0e8

Please sign in to comment.