/
map.js
107 lines (101 loc) · 2.5 KB
/
map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import Spec from "./spec";
import keys from "./keys";
import {
valid,
explain,
dt,
specize,
getName,
getAllKeys,
undefinedPredicateWarning
} from "../util";
import * as p from "../predicates";
import { invalid, optional } from "../symbols";
export class Map extends Spec {
conform(value) {
if (p.object(value)) {
if (this.options.requiredKeys.length > 0) {
const keySpec = keys(
`Keys(${this.name})`,
...this.options.requiredKeys
);
if (!valid(keySpec, value)) {
return invalid;
}
}
const ret = {};
for (const key of getAllKeys(value)) {
const v = value[key];
const spec =
this.options.requiredSpecs[key] ||
this.options.optionalSpecs[key] ||
null;
if (spec !== null) {
const conformed = dt(spec, v);
if (conformed === invalid) {
return invalid;
}
ret[key] = conformed;
} else {
ret[key] = v;
}
}
return ret;
}
return invalid;
}
toString() {
return this.name || `Map`;
}
explain(path, via, value) {
if (this.options.requiredKeys.length > 0) {
const ks = keys(`Keys(${this.name})`, ...this.options.requiredKeys);
if (!valid(ks, value)) {
return explain(ks, path, [...via, getName(this)], value);
}
}
return getAllKeys(value).map(key => {
const v = value[key];
const hasSpec =
this.options.requiredSpecs[key] ||
this.options.optionalSpecs[key] ||
null;
if (hasSpec) {
const spec = specize(hasSpec);
if (!valid(spec, v)) {
return explain(
spec,
[...path, key],
[...via, getName(this)],
value[key]
);
}
}
return null;
});
}
}
export default function map(name, shape) {
if (!p.string(name)) {
throw new Error(`Name ${name} must be a string`);
}
if (!shape || !p.obj(shape)) {
throw new Error(`Cannot use Map spec with shape ${shape}`);
}
const optionalSpecs = shape[optional] || {};
const optionalKeys = getAllKeys(optionalSpecs);
const requiredSpecs = shape;
const requiredKeys = getAllKeys(shape);
if (requiredKeys.length > 0) {
undefinedPredicateWarning(name, shape);
}
if (optionalKeys.length > 0) {
undefinedPredicateWarning(name, shape[optional]);
}
return new Map(name, {
requiredKeys,
requiredSpecs,
optionalKeys,
optionalSpecs
});
}