Skip to content

Commit

Permalink
add Wrap member to DecodeError + add withMessage combinator to `D…
Browse files Browse the repository at this point in the history
…ecoder` and `TaskDecoder`
  • Loading branch information
gcanti committed Jul 16, 2020
1 parent a7283bd commit 923bd59
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@

- **Polish**
- remove `Object.freeze` calls, closes #497 (@gcanti)
- **Experimental**
- `DecodeError`
- add `Wrap` member (@gcanti)
- `Decoder`
- add `withMessage` combinator (@gcanti)
- `TaskDecoder`
- add `withMessage` combinator (@gcanti)

# 2.2.8

Expand Down
29 changes: 28 additions & 1 deletion docs/modules/DecodeError.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Added in v2.2.7
- [lazy](#lazy)
- [leaf](#leaf)
- [member](#member)
- [wrap](#wrap)
- [destructors](#destructors)
- [fold](#fold)
- [instances](#instances)
Expand All @@ -37,6 +38,7 @@ Added in v2.2.7
- [Lazy (interface)](#lazy-interface)
- [Leaf (interface)](#leaf-interface)
- [Member (interface)](#member-interface)
- [Wrap (interface)](#wrap-interface)
- [optional](#optional)
- [required](#required)

Expand Down Expand Up @@ -94,6 +96,16 @@ export declare const member: <E>(index: number, errors: FS.FreeSemigroup<DecodeE
Added in v2.2.7
## wrap
**Signature**
```ts
export declare const wrap: <E>(error: E, errors: FS.FreeSemigroup<DecodeError<E>>) => DecodeError<E>
```
Added in v2.2.9
# destructors
## fold
Expand All @@ -107,6 +119,7 @@ export declare const fold: <E, R>(patterns: {
Index: (index: number, kind: Kind, errors: FS.FreeSemigroup<DecodeError<E>>) => R
Member: (index: number, errors: FS.FreeSemigroup<DecodeError<E>>) => R
Lazy: (id: string, errors: FS.FreeSemigroup<DecodeError<E>>) => R
Wrap: (error: E, errors: FS.FreeSemigroup<DecodeError<E>>) => R
}) => (e: DecodeError<E>) => R
```
Expand All @@ -131,7 +144,7 @@ Added in v2.2.7
**Signature**

```ts
export type DecodeError<E> = Leaf<E> | Key<E> | Index<E> | Member<E> | Lazy<E>
export type DecodeError<E> = Leaf<E> | Key<E> | Index<E> | Member<E> | Lazy<E> | Wrap<E>
```

Added in v2.2.7
Expand Down Expand Up @@ -218,6 +231,20 @@ export interface Member<E> {

Added in v2.2.7

## Wrap (interface)

**Signature**

```ts
export interface Wrap<E> {
readonly _tag: 'Wrap'
readonly error: E
readonly errors: FS.FreeSemigroup<DecodeError<E>>
}
```

Added in v2.2.9

## optional

**Signature**
Expand Down
13 changes: 13 additions & 0 deletions docs/modules/Decoder.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Added in v2.2.7
- [tuple](#tuple)
- [type](#type)
- [union](#union)
- [withMessage](#withmessage)
- [constructors](#constructors)
- [fromGuard](#fromguard)
- [fromRefinement](#fromrefinement)
Expand Down Expand Up @@ -398,6 +399,18 @@ export declare const union: <MS extends readonly [Decoder<any, any>, ...Decoder<
Added in v2.2.7
## withMessage
**Signature**
```ts
export declare const withMessage: <I>(
message: (input: I, e: FS.FreeSemigroup<DE.DecodeError<string>>) => string
) => <A>(decoder: Decoder<I, A>) => Decoder<I, A>
```
Added in v2.2.9
# constructors
## fromGuard
Expand Down
13 changes: 13 additions & 0 deletions docs/modules/TaskDecoder.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Added in v2.2.7
- [tuple](#tuple)
- [type](#type)
- [union](#union)
- [withMessage](#withmessage)
- [constructors](#constructors)
- [fromDecoder](#fromdecoder)
- [fromGuard](#fromguard)
Expand Down Expand Up @@ -401,6 +402,18 @@ export declare const union: <MS extends readonly [TaskDecoder<any, any>, ...Task
Added in v2.2.7
## withMessage
**Signature**
```ts
export declare const withMessage: <I>(
message: (input: I, e: FS.FreeSemigroup<DE.DecodeError<string>>) => string
) => <A>(decoder: TaskDecoder<I, A>) => TaskDecoder<I, A>
```
Added in v2.2.9
# constructors
## fromDecoder
Expand Down
25 changes: 24 additions & 1 deletion src/DecodeError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,21 @@ export interface Lazy<E> {
readonly errors: FS.FreeSemigroup<DecodeError<E>>
}

/**
* @category model
* @since 2.2.9
*/
export interface Wrap<E> {
readonly _tag: 'Wrap'
readonly error: E
readonly errors: FS.FreeSemigroup<DecodeError<E>>
}

/**
* @category model
* @since 2.2.7
*/
export type DecodeError<E> = Leaf<E> | Key<E> | Index<E> | Member<E> | Lazy<E>
export type DecodeError<E> = Leaf<E> | Key<E> | Index<E> | Member<E> | Lazy<E> | Wrap<E>

/**
* @category constructors
Expand Down Expand Up @@ -135,6 +145,16 @@ export const lazy = <E>(id: string, errors: FS.FreeSemigroup<DecodeError<E>>): D
errors
})

/**
* @category constructors
* @since 2.2.9
*/
export const wrap = <E>(error: E, errors: FS.FreeSemigroup<DecodeError<E>>): DecodeError<E> => ({
_tag: 'Wrap',
error,
errors
})

/**
* @category destructors
* @since 2.2.7
Expand All @@ -145,6 +165,7 @@ export const fold = <E, R>(patterns: {
Index: (index: number, kind: Kind, errors: FS.FreeSemigroup<DecodeError<E>>) => R
Member: (index: number, errors: FS.FreeSemigroup<DecodeError<E>>) => R
Lazy: (id: string, errors: FS.FreeSemigroup<DecodeError<E>>) => R
Wrap: (error: E, errors: FS.FreeSemigroup<DecodeError<E>>) => R
}): ((e: DecodeError<E>) => R) => {
const f = (e: DecodeError<E>): R => {
switch (e._tag) {
Expand All @@ -158,6 +179,8 @@ export const fold = <E, R>(patterns: {
return patterns.Member(e.index, e.errors)
case 'Lazy':
return patterns.Lazy(e.id, e.errors)
case 'Wrap':
return patterns.Wrap(e.error, e.errors)
}
}
return f
Expand Down
12 changes: 11 additions & 1 deletion src/Decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ export const mapLeftWithInput: <I>(
/*#__PURE__*/
K.mapLeftWithInput(M)

/**
* @category combinators
* @since 2.2.9
*/
export const withMessage = <I>(
message: (input: I, e: DecodeError) => string
): (<A>(decoder: Decoder<I, A>) => Decoder<I, A>) =>
mapLeftWithInput((input, e) => FS.of(DE.wrap(message(input, e), e)))

/**
* @category combinators
* @since 2.2.7
Expand Down Expand Up @@ -536,7 +545,8 @@ const toTree: (e: DE.DecodeError<string>) => Tree<string> = DE.fold({
Key: (key, kind, errors) => make(`${kind} property ${JSON.stringify(key)}`, toForest(errors)),
Index: (index, kind, errors) => make(`${kind} index ${index}`, toForest(errors)),
Member: (index, errors) => make(`member ${index}`, toForest(errors)),
Lazy: (id, errors) => make(`lazy type ${id}`, toForest(errors))
Lazy: (id, errors) => make(`lazy type ${id}`, toForest(errors)),
Wrap: (error, errors) => make(error, toForest(errors))
})

const toForest: (e: DecodeError) => ReadonlyArray<Tree<string>> = FS.fold(
Expand Down
9 changes: 9 additions & 0 deletions src/TaskDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,15 @@ export const mapLeftWithInput: <I>(
/*#__PURE__*/
K.mapLeftWithInput(M)

/**
* @category combinators
* @since 2.2.9
*/
export const withMessage = <I>(
message: (input: I, e: DecodeError) => string
): (<A>(decoder: TaskDecoder<I, A>) => TaskDecoder<I, A>) =>
mapLeftWithInput((input, e) => FS.of(DE.wrap(message(input, e), e)))

/**
* @category combinators
* @since 2.2.7
Expand Down
18 changes: 18 additions & 0 deletions test/Decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,24 @@ describe('Decoder', () => {
assert.deepStrictEqual(decoder.decode('a'), E.left(FS.of(DE.leaf('a', 'not a number'))))
})

it('withMessage', () => {
const decoder = pipe(
_.type({
name: _.string,
age: _.number
}),
_.withMessage(() => 'Person')
)
assert.deepStrictEqual(
pipe(decoder.decode({}), E.mapLeft(_.draw)),
E.left(`Person
├─ required property "name"
│ └─ cannot decode undefined, should be string
└─ required property "age"
└─ cannot decode undefined, should be number`)
)
})

it('compose', () => {
interface IntBrand {
readonly Int: unique symbol
Expand Down
18 changes: 18 additions & 0 deletions test/TaskDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ describe('UnknownTaskDecoder', () => {
assert.deepStrictEqual(await decoder.decode('a')(), D.failure('a', 'not a number'))
})

it('withMessage', async () => {
const decoder = pipe(
_.type({
name: _.string,
age: _.number
}),
_.withMessage(() => 'Person')
)
assert.deepStrictEqual(
await pipe(decoder.decode({}), TE.mapLeft(_.draw))(),
E.left(`Person
├─ required property "name"
│ └─ cannot decode undefined, should be string
└─ required property "age"
└─ cannot decode undefined, should be number`)
)
})

it('compose', async () => {
interface IntBrand {
readonly Int: unique symbol
Expand Down

0 comments on commit 923bd59

Please sign in to comment.