-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #35 from paperhive/feature/object-map
Add objectMap()
- Loading branch information
Showing
5 changed files
with
108 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { assert } from 'chai' | ||
|
||
import { objectMap } from './object-map' | ||
import { string } from './string' | ||
import { branchError, leafError } from './errors' | ||
import { failure, success } from './result' | ||
|
||
describe('objectMap()', () => { | ||
it('should return an error if value is not an object', () => | ||
assert.deepStrictEqual( | ||
objectMap(string())(null), | ||
failure(leafError(null, 'Not an object.')) | ||
)) | ||
|
||
it('should return an error if object has a value that does not validate', () => { | ||
const value = { foo: 1337 } | ||
assert.deepStrictEqual( | ||
objectMap(string())(value), | ||
failure( | ||
branchError(value, [ | ||
{ key: 'foo', error: leafError(1337, 'Not a string.') }, | ||
]) | ||
) | ||
) | ||
}) | ||
|
||
it('should return all errors if requested and object has two value that do not validate', () => { | ||
const value = { foo: 1337, bar: true } | ||
assert.deepStrictEqual( | ||
objectMap(string(), { allErrors: true })(value), | ||
failure( | ||
branchError(value, [ | ||
{ key: 'foo', error: leafError(1337, 'Not a string.') }, | ||
{ key: 'bar', error: leafError(true, 'Not a string.') }, | ||
]) | ||
) | ||
) | ||
}) | ||
|
||
it('should validate an object', () => { | ||
const value = { foo: 'bar' } | ||
assert.deepStrictEqual(objectMap(string())(value), success(value)) | ||
}) | ||
|
||
it('should validate an object with allErrors', () => { | ||
const value = { foo: 'bar' } | ||
assert.deepStrictEqual( | ||
objectMap(string(), { allErrors: true })(value), | ||
success(value) | ||
) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { partitionMap, traverse } from 'fp-ts/lib/Array' | ||
import { Either, either, isLeft, left, right } from 'fp-ts/lib/Either' | ||
import { branchError, ChildError, success } from '.' | ||
import { leafError } from './errors' | ||
import { failure } from './result' | ||
import { Validator } from './transformer' | ||
|
||
export interface ObjectMapOptions { | ||
allErrors?: boolean | ||
} | ||
|
||
export function objectMap<R>( | ||
valueValidator: Validator<R>, | ||
{ allErrors }: ObjectMapOptions = {} | ||
): Validator<{ [k in string]?: R }> { | ||
function validateEntry([key, v]: [string, unknown]): Either< | ||
ChildError, | ||
[string, R] | ||
> { | ||
const result = valueValidator(v) | ||
if (isLeft(result)) return left({ key, error: result.left }) | ||
return right([key, result.right]) | ||
} | ||
|
||
return (value) => { | ||
if (typeof value !== 'object' || value === null) | ||
return failure(leafError(value, 'Not an object.')) | ||
|
||
const entries = Object.entries(value) | ||
|
||
if (allErrors) { | ||
const results = partitionMap(validateEntry)(entries) | ||
if (results.left.length > 0) | ||
return failure(branchError(value, results.left)) | ||
return success(Object.fromEntries(results.right)) | ||
} | ||
|
||
const result = traverse(either)(validateEntry)(entries) | ||
if (isLeft(result)) return failure(branchError(value, [result.left])) | ||
return success(Object.fromEntries(result.right)) | ||
} | ||
} |