-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmap.js
109 lines (99 loc) · 2.7 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
108
109
'use strict';
const { typeOf, failSchemaValidation, assertValidator } = require('./util');
const { mapVerifyer } = require('./map-verifyer');
const { validator } = require('./validator');
/**
* @typedef {import('./verifyer').Value} Value
*/
/**
* @template {Value} V
* @typedef {import('./validator').Validator<V>} Validator
*/
/**
* @template {Validator<*>} V
* @typedef {import('./validator').VParameter<V>} VParameter
*/
/**
* @typedef {import('./literal').Literal<*>} Literal
*/
exports.map = map;
/* eslint-disable jsdoc/valid-types */
/**
* @template {Validator<*>} K
* @typedef {K extends Literal ? string : VParameter<K>} VKey
*/
/* eslint-enable */
/**
* @template {Validator<*>} K
* @template {Validator<*>} V
* @typedef {Validator<Record<VKey<K>, VParameter<V>>> & { type: 'Map', keys: K, values: V }} VMap
*/
/**
* @template {Validator<*>} K
* @template {Validator<*>} V
* @param {K} keyTest
* @param {V} valueTest
* @returns {VMap<K, V>}
*/
function map(keyTest, valueTest) {
assertValidator(keyTest);
assertValidator(valueTest);
const test = /** @type {VMap<K, V>} */ (
validator(
mapTester(keyTest, valueTest),
mapValidatorName(keyTest, valueTest),
mapVerifyer(keyVerifyer(keyTest), valueTest)
)
);
test.type = 'Map';
test.keys = keyTest;
test.values = valueTest;
return test;
}
function mapTester(keyTest, valueTest) {
const actualKeyTest =
keyTest.type === 'integer' ? integerKeyTest(keyTest) : keyTest;
return (value) =>
typeOf(value) === 'Object' &&
Object.keys(value).every(
(key) => actualKeyTest(key) && valueTest(value[key])
);
}
function integerKeyTest(keyTest) {
return (key) => {
const integer_key = parseInt(key, 10);
return String(integer_key) === key && keyTest(integer_key);
};
}
function mapValidatorName(keyTest, valueTest) {
return () => `{${keyTest}:${valueTest}}`;
}
function keyVerifyer(keyTest) {
return (key, options, base) => {
let actual_key = key;
if (keyTest.type === 'integer') {
actual_key = parseInt(key, 10);
if (String(actual_key) !== key) {
failSchemaValidation(
new TypeError(
base && base !== key
? `Expected key "${key}" in "${base}" to be an integer string`
: `Expected key "${key}" to be an integer string`
),
options.error_code
);
}
}
if (keyTest(actual_key, options, base) === false) {
failSchemaValidation(
new TypeError(
base && base !== key
? `Expected key "${key}" in "${base}" to be ${keyTest}`
: `Expected key "${key}" to be ${keyTest}`
),
options.error_code
);
}
return base;
};
}