-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
195 lines (176 loc) · 5.32 KB
/
index.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
'use strict';
/**
* FUSE
* FunctionSelect: Return a function based on a condition.
* @version 0.10.0
* @author Muthu Kumar (MKRhere)
*/
/**
* @callback predicate
* @param {any} value The selected Fuse value
* @returns {boolean} The Boolean result of the test
*/
/**
* @callback consequent
* @param {any} value The selected Fuse value
* @param {...any} args An arbitrary array of arguments
* @returns {any}
*/
/**
* Creates a FuseItem instance with a value and optional resolve function.
* FuseIterable constructor uses it internally, and Fuse extends FuseItem.
* Not exposed.
* @class FuseItem
*/
class FuseItem {
/**
* @param {any} value The input value
* @param {function} resolve Optional resolve function
* @constructs FuseItem
*/
constructor(value, resolve) {
this.value = value;
if (resolve) {
this.resolve = (...args) => resolve(this.value, ...args);
this.resolved = true;
}
}
/**
* Fallback resolve prototype. Returns null when called.
* Used in case a resolve is never found.
* @returns {null} null
* @memberof FuseItem
*/
resolve() {
return null;
}
}
/**
* Creates a FuseIterable instance from an array or iterable.
* @class FuseIterable
* @param {iterable} values An iterable expression as the switch
* @param {Array<function>} tests Array of { test, consequent } objects
* @param {function} tests[].predicate Test function
* @param {function} tests[].consequent Consequent function
* @constructs FuseIterable
*/
class FuseIterable {
constructor(values, conditionals) {
this.values = Array.from(values).map(value =>
value instanceof FuseItem
? value
: new FuseItem(value));
this.conditionals = conditionals || [];
}
/**
* Accepts a test and consequent function each and returns a new
* FuseIterable instance.
* FuseIterable.prototype.for works a little differently than
* Fuse.prototype.for, by lazy accumulating the tests and
* resolving all the values when .resolve() is called.
* @param {callback} predicate A test callback function
* @param {callback} consequent Consequent callback function
* @returns {FuseIterable} An instance of FuseIterable
* @memberof FuseIterable
*/
on(predicate, consequent) {
return new FuseIterable(this.values, [
...this.conditionals,
{ predicate, consequent }
]);
}
/**
* Accepts a list of tuples as arguments and returns a new
* FuseIterable instance. An alternative to chaining multiple
* FuseIterable.prototype.on methods.
* @memberOf FuseIterable
* @param {...Array.<function>} tuples -
* Array of [ predicate, consequent ] pairs
* @returns {FuseIterable} An instance of FuseIterable
*/
onField(...tuples) {
const conditionals = tuples.map(conditional =>
({
predicate: conditional[0],
consequent: conditional[1]
}));
return new FuseIterable(this.values, conditionals);
}
/**
* Accepts parameters during resolve time and passes them along with
* each value into the winning consequent function.
* @param {...any} args Any number of arguments
* @returns {(any|null)} Resolved value or null if it was unresolved
*/
resolve(...args) {
return this.values.map(item => {
const resolver = this.conditionals.find(conditional =>
conditional.predicate(item.value)
? conditional.consequent
: null
);
return resolver
? resolver.consequent(item.value, ...args)
: null;
});
}
}
/**
* Creates a new Fuse instance.
* @class Fuse
* @extends {FuseItem}
*/
class Fuse extends FuseItem {
/**
* Accepts a test and consequent function each and returns a new
* Fuse or FuseIterable instance.
* @param {callback} predicate A test callback function
* @param {callback} consequent Consequent callback function
* @returns {Fuse} Returns a new Fuse instance
* @memberof Fuse
*/
on(predicate, consequent) {
/* If a resolve exists, just pass on the instance
until .resolve() is called */
if (this.resolved) return this;
if (predicate(this.value)) return new Fuse(this.value, consequent);
/* If the test doesn't pass, just pass the Fuse
instance along the chain until a test passes,
or .resolve() is called */
return this;
}
/**
* Accepts a consequent function which automatically becomes resolve.
* Does not return a Fuse instance as .on after .else would be useless.
* @param {callback} consequent Consequent callback function
* @returns {Fuse} Returns new FuseItem instance
*/
else(consequent) {
/* If a resolve exists, just pass on the instance
until .resolve() is called */
if (this.resolved) return this;
return new FuseItem(this.value, consequent);
}
/**
* Accepts a value instead of a test function, and checks for strict
* equality with this.value.
* @param {any} value Any value to check against this.value
* @param {function} consequent Consequent callback function
* @returns {Fuse} An instance of Fuse
*/
is(value, consequent) {
return this.on(() => value === this.value, consequent);
}
/**
* Accepts a value instead of a test function, and checks for strict
* inequality with this.value.
* @param {any} value Any value to check against this.value
* @param {function} consequent Consequent callback function
* @returns {Fuse} An instance of Fuse
*/
not(value, consequent) {
return this.on(() => value !== this.value, consequent);
}
}
module.exports = Fuse;
module.exports.FuseIterable = FuseIterable;