rsult 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/jest.config.js +6 -0
- package/package.json +18 -0
- package/readme.md +313 -0
- package/rsult-test.svg +1 -0
- package/rsult.svg +1 -0
- package/src/lib.ts +2 -0
- package/src/option.test.ts +432 -0
- package/src/option.ts +521 -0
- package/src/result.test.ts +459 -0
- package/src/result.ts +646 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Some,
|
|
3
|
+
None,
|
|
4
|
+
option_from_nullable,
|
|
5
|
+
option_from_promise,
|
|
6
|
+
Option,
|
|
7
|
+
} from './option';
|
|
8
|
+
|
|
9
|
+
describe('Option', () => {
|
|
10
|
+
describe('Check Methods', () => {
|
|
11
|
+
it('is_some() should return true for Some', () => {
|
|
12
|
+
const option = Some(5);
|
|
13
|
+
expect(option.is_some()).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('is_some() should return false for None', () => {
|
|
17
|
+
const option = None();
|
|
18
|
+
expect(option.is_some()).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('is_some_and() should return true if the option is Some and the condition is met', () => {
|
|
22
|
+
const option = Some(5);
|
|
23
|
+
expect(option.is_some_and(x => x > 3)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('is_some_and() should return false if the option is Some and the condition is not met', () => {
|
|
27
|
+
const option = Some(2);
|
|
28
|
+
expect(option.is_some_and(x => x > 3)).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('is_some_and() should return false for None', () => {
|
|
32
|
+
const option = None<number>();
|
|
33
|
+
expect(option.is_some_and(x => x > 3)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('is_none() should return false for Some', () => {
|
|
37
|
+
const option = Some(5);
|
|
38
|
+
expect(option.is_none()).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('is_none() should return true for None', () => {
|
|
42
|
+
const option = None();
|
|
43
|
+
expect(option.is_none()).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('Transform Methods', () => {
|
|
49
|
+
it('map() should transform Some value correctly', () => {
|
|
50
|
+
const option = Some(5);
|
|
51
|
+
const newOption = option.map(x => x * 2);
|
|
52
|
+
expect(newOption.unwrap()).toBe(10);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('map() should not affect None', () => {
|
|
56
|
+
const option = None();
|
|
57
|
+
const newOption = option.map(x => x * 2);
|
|
58
|
+
expect(() => newOption.unwrap()).toThrow();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('map_or() should transform Some value and return transformed value', () => {
|
|
62
|
+
const option = Some(5);
|
|
63
|
+
const result = option.map_or(0, x => x * 2);
|
|
64
|
+
expect(result).toBe(10);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('map_or() should return default value for None', () => {
|
|
68
|
+
const option = None<number>();
|
|
69
|
+
const result = option.map_or(0, x => x * 2);
|
|
70
|
+
expect(result).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('Expect and Unwrap Methods', () => {
|
|
75
|
+
it('expect() should return the value for Some', () => {
|
|
76
|
+
const option = Some(5);
|
|
77
|
+
expect(option.expect("Expected a value!")).toBe(5);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('expect() should throw an error for None', () => {
|
|
81
|
+
const option = None();
|
|
82
|
+
expect(() => option.expect("Expected a value!")).toThrow("Expected a value!");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('unwrap() should return the value for Some', () => {
|
|
86
|
+
const option = Some(10);
|
|
87
|
+
expect(option.unwrap()).toBe(10);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('unwrap() should throw an error for None', () => {
|
|
91
|
+
const option = None();
|
|
92
|
+
expect(() => option.unwrap()).toThrow();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('unwrap_or() should return the value for Some', () => {
|
|
96
|
+
const option = Some(15);
|
|
97
|
+
expect(option.unwrap_or(20)).toBe(15);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('unwrap_or() should return the provided default value for None', () => {
|
|
101
|
+
const option = None();
|
|
102
|
+
expect(option.unwrap_or(20)).toBe(20);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('unwrap_or_else() should return the value for Some', () => {
|
|
106
|
+
const option = Some(15);
|
|
107
|
+
expect(option.unwrap_or_else(() => 20)).toBe(15);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('unwrap_or_else() should return the result of the function for None', () => {
|
|
111
|
+
const option = None();
|
|
112
|
+
expect(option.unwrap_or_else(() => 20)).toBe(20);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('unwrap_or_default() should return the value for Some', () => {
|
|
116
|
+
const option = Some(15);
|
|
117
|
+
expect(option.unwrap_or_default()).toBe(15);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('unwrap_or_default() should return the default value for the type for None', () => {
|
|
121
|
+
const option = None<number>();
|
|
122
|
+
// this should be 0 for number, but TypeScript can't do
|
|
123
|
+
// runtime checks for default values based on type
|
|
124
|
+
expect(option.unwrap_or_default()).toBe(null);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('Combine Methods', () => {
|
|
129
|
+
it('and() should return the passed Option if the first Option is Some', () => {
|
|
130
|
+
const opt1 = Some(5);
|
|
131
|
+
const opt2 = Some(10);
|
|
132
|
+
const result = opt1.and(opt2);
|
|
133
|
+
expect(result.unwrap()).toBe(10);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('and() should return None if the first Option is None', () => {
|
|
137
|
+
const opt1 = None();
|
|
138
|
+
const opt2 = Some(10);
|
|
139
|
+
const result = opt1.and(opt2);
|
|
140
|
+
expect(() => result.unwrap()).toThrow();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('and_then() should apply function and return Some if the first Option is Some', () => {
|
|
144
|
+
const opt = Some(5);
|
|
145
|
+
const result = opt.and_then(x => Some(x * 2));
|
|
146
|
+
expect(result.unwrap()).toBe(10);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('and_then() should return None if the first Option is None', () => {
|
|
150
|
+
const opt = None();
|
|
151
|
+
const result = opt.and_then(x => Some(x * 2));
|
|
152
|
+
expect(() => result.unwrap()).toThrow();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('or() should return the first Option if it is Some', () => {
|
|
156
|
+
const opt1 = Some(5);
|
|
157
|
+
const opt2 = Some(10);
|
|
158
|
+
const result = opt1.or(opt2);
|
|
159
|
+
expect(result.unwrap()).toBe(5);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('or() should return the second Option if the first Option is None', () => {
|
|
163
|
+
const opt1 = None();
|
|
164
|
+
const opt2 = Some(10);
|
|
165
|
+
const result = opt1.or(opt2);
|
|
166
|
+
expect(result.unwrap()).toBe(10);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('or_else() should return the first Option if it is Some', () => {
|
|
170
|
+
const opt1 = Some(5);
|
|
171
|
+
const result = opt1.or_else(() => Some(10));
|
|
172
|
+
expect(result.unwrap()).toBe(5);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('or_else() should return the result of the function if the first Option is None', () => {
|
|
176
|
+
const opt1 = None();
|
|
177
|
+
const result = opt1.or_else(() => Some(10));
|
|
178
|
+
expect(result.unwrap()).toBe(10);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('xor() should return None if both Options are Some', () => {
|
|
182
|
+
const opt1 = Some(5);
|
|
183
|
+
const opt2 = Some(10);
|
|
184
|
+
const result = opt1.xor(opt2);
|
|
185
|
+
expect(() => result.unwrap()).toThrow();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('xor() should return Some if only one of the Options is Some', () => {
|
|
189
|
+
const opt1 = Some(5);
|
|
190
|
+
const opt2 = None<number>();
|
|
191
|
+
const result = opt1.xor(opt2);
|
|
192
|
+
expect(result.unwrap()).toBe(5);
|
|
193
|
+
|
|
194
|
+
const opt3 = None();
|
|
195
|
+
const opt4 = Some(10);
|
|
196
|
+
const result2 = opt3.xor(opt4);
|
|
197
|
+
expect(result2.unwrap()).toBe(10);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('xor() should return None if both Options are None', () => {
|
|
201
|
+
const opt1 = None();
|
|
202
|
+
const opt2 = None();
|
|
203
|
+
const result = opt1.xor(opt2);
|
|
204
|
+
expect(() => result.unwrap()).toThrow();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('Mutate Methods', () => {
|
|
209
|
+
it('take() should take the value from Some, leaving None', () => {
|
|
210
|
+
const option = Some(5);
|
|
211
|
+
const taken = option.take();
|
|
212
|
+
expect(taken.unwrap()).toBe(5);
|
|
213
|
+
|
|
214
|
+
// this is not possible in the current TypeScript implementation
|
|
215
|
+
// as the value is moved out of the Option, but the Option is still
|
|
216
|
+
// "OptionSome"
|
|
217
|
+
//expect(option.is_none()).toBe(true);
|
|
218
|
+
expect(option.is_some()).toBe(true);
|
|
219
|
+
expect(option.unwrap()).toBe(undefined);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('take() should do nothing on None, returning None', () => {
|
|
223
|
+
const option = None();
|
|
224
|
+
const taken = option.take();
|
|
225
|
+
expect(() => taken.unwrap()).toThrow();
|
|
226
|
+
expect(option.is_none()).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('take_if() should take the value if the condition is met, leaving None', () => {
|
|
230
|
+
const option = Some(5);
|
|
231
|
+
const taken = option.take_if(x => x > 3);
|
|
232
|
+
expect(taken.unwrap()).toBe(5);
|
|
233
|
+
// this is not possible in the current TypeScript implementation
|
|
234
|
+
// as the value is moved out of the Option, but the Option is still
|
|
235
|
+
// "OptionSome"
|
|
236
|
+
//expect(option.is_none()).toBe(true);
|
|
237
|
+
expect(option.is_some()).toBe(true);
|
|
238
|
+
expect(option.unwrap()).toBe(undefined);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('take_if() should leave the option untouched if condition is not met', () => {
|
|
242
|
+
const option = Some(2);
|
|
243
|
+
const taken = option.take_if(x => x > 3);
|
|
244
|
+
expect(() => taken.unwrap()).toThrow();
|
|
245
|
+
expect(option.is_some()).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('take_if() should do nothing on None, returning None', () => {
|
|
249
|
+
const option = None<number>();
|
|
250
|
+
const taken = option.take_if(x => x > 3);
|
|
251
|
+
expect(() => taken.unwrap()).toThrow();
|
|
252
|
+
expect(option.is_none()).toBe(true);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('replace() should replace the value of Some, returning the old value', () => {
|
|
256
|
+
const option = Some(5);
|
|
257
|
+
const oldValue = option.replace(10);
|
|
258
|
+
expect(oldValue.unwrap()).toBe(5);
|
|
259
|
+
expect(option.unwrap()).toBe(10);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('replace() should place a value in None, returning None', () => {
|
|
263
|
+
const option = None();
|
|
264
|
+
const oldValue = option.replace(10);
|
|
265
|
+
expect(() => option.unwrap()).toThrow();
|
|
266
|
+
expect(oldValue.unwrap()).toBe(10);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe('Zip Methods', () => {
|
|
271
|
+
it('zip() combines Some values into a tuple', () => {
|
|
272
|
+
const opt1 = Some(5);
|
|
273
|
+
const opt2 = Some("hello");
|
|
274
|
+
const zipped = opt1.zip(opt2);
|
|
275
|
+
expect(zipped.unwrap()).toEqual([5, "hello"]);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('zip() returns None if either Option is None', () => {
|
|
279
|
+
const opt1 = Some(5);
|
|
280
|
+
const opt2 = None<string>();
|
|
281
|
+
const zipped = opt1.zip(opt2);
|
|
282
|
+
expect(() => zipped.unwrap()).toThrow();
|
|
283
|
+
|
|
284
|
+
const opt3 = None<number>();
|
|
285
|
+
const opt4 = Some("hello");
|
|
286
|
+
const zipped2 = opt3.zip(opt4);
|
|
287
|
+
expect(() => zipped2.unwrap()).toThrow();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('zip_with() combines Some values with a function', () => {
|
|
291
|
+
const opt1 = Some(5);
|
|
292
|
+
const opt2 = Some(10);
|
|
293
|
+
const added = opt1.zip_with(opt2, (a, b) => a + b);
|
|
294
|
+
expect(added.unwrap()).toBe(15);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('zip_with() returns None if either Option is None', () => {
|
|
298
|
+
const opt1 = Some(5);
|
|
299
|
+
const opt2 = None<number>();
|
|
300
|
+
const shouldBeNone = opt1.zip_with(opt2, (a, b) => a + b);
|
|
301
|
+
expect(() => shouldBeNone.unwrap()).toThrow();
|
|
302
|
+
|
|
303
|
+
const opt3 = None<number>();
|
|
304
|
+
const opt4 = Some(10);
|
|
305
|
+
const shouldBeNone2 = opt3.zip_with(opt4, (a, b) => a + b);
|
|
306
|
+
expect(() => shouldBeNone2.unwrap()).toThrow();
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe('Filter Method', () => {
|
|
311
|
+
it('filter() should return the same Option if the predicate is true for Some', () => {
|
|
312
|
+
const option = Some(5);
|
|
313
|
+
const filteredOption = option.filter(x => x > 3);
|
|
314
|
+
expect(filteredOption.unwrap()).toBe(5);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('filter() should return None if the predicate is false for Some', () => {
|
|
318
|
+
const option = Some(2);
|
|
319
|
+
const filteredOption = option.filter(x => x > 3);
|
|
320
|
+
expect(() => filteredOption.unwrap()).toThrow();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('filter() should return None if called on None', () => {
|
|
324
|
+
const option = None<number>();
|
|
325
|
+
const filteredOption = option.filter(x => x > 3);
|
|
326
|
+
expect(() => filteredOption.unwrap()).toThrow();
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe('Flatten Method', () => {
|
|
331
|
+
it('flatten() should return the inner option if the outer option is Some and contains another Some', () => {
|
|
332
|
+
const option = Some(Some(5));
|
|
333
|
+
const flattened = option.flatten();
|
|
334
|
+
expect(flattened.unwrap()).toBe(5);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('flatten() should return None if the outer option is Some but contains None', () => {
|
|
338
|
+
const option = Some(None<number>());
|
|
339
|
+
const flattened = option.flatten();
|
|
340
|
+
expect(() => flattened.unwrap()).toThrow();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('flatten() should return None if the outer option is None', () => {
|
|
344
|
+
const option = None<Option<number>>();
|
|
345
|
+
const flattened = option.flatten();
|
|
346
|
+
expect(() => flattened.unwrap()).toThrow();
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
describe('Utility Methods', () => {
|
|
351
|
+
describe('Core', () => {
|
|
352
|
+
it('Some() should create a Some Option', () => {
|
|
353
|
+
const option = Some(5);
|
|
354
|
+
expect(option.unwrap()).toBe(5);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('None() should create a None Option', () => {
|
|
358
|
+
const option = None();
|
|
359
|
+
expect(() => option.unwrap()).toThrow();
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
describe('option_from_nullable', () => {
|
|
364
|
+
it('should return Some for non-null values', () => {
|
|
365
|
+
const option = option_from_nullable(5);
|
|
366
|
+
expect(option.unwrap()).toBe(5);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should return None for null', () => {
|
|
370
|
+
const option = option_from_nullable(null);
|
|
371
|
+
expect(option.is_none()).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should return None for undefined', () => {
|
|
375
|
+
const option = option_from_nullable(undefined);
|
|
376
|
+
expect(option.is_none()).toBe(true);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should return Some for non-null string values', () => {
|
|
380
|
+
const option = option_from_nullable("test");
|
|
381
|
+
expect(option.unwrap()).toBe("test");
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should return Some for empty string', () => {
|
|
385
|
+
const option = option_from_nullable("");
|
|
386
|
+
expect(option.unwrap()).toBe("");
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should return Some for boolean true', () => {
|
|
390
|
+
const option = option_from_nullable(true);
|
|
391
|
+
expect(option.unwrap()).toBe(true);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('should return Some for boolean false', () => {
|
|
395
|
+
const option = option_from_nullable(false);
|
|
396
|
+
expect(option.unwrap()).toBe(false);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
describe('option_from_promise', () => {
|
|
401
|
+
it('should return Some on promise resolution', async () => {
|
|
402
|
+
const promise = Promise.resolve(5);
|
|
403
|
+
const option = await option_from_promise(promise);
|
|
404
|
+
expect(option.unwrap()).toBe(5);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('should return None on promise rejection', async () => {
|
|
408
|
+
const promise = Promise.reject('Error');
|
|
409
|
+
const option = await option_from_promise(promise);
|
|
410
|
+
expect(option.is_none()).toBe(true);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should return Some on promise resolution with string', async () => {
|
|
414
|
+
const promise = Promise.resolve("test");
|
|
415
|
+
const option = await option_from_promise(promise);
|
|
416
|
+
expect(option.unwrap()).toBe("test");
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('should return Some on promise resolution with boolean', async () => {
|
|
420
|
+
const promise = Promise.resolve(true);
|
|
421
|
+
const option = await option_from_promise(promise);
|
|
422
|
+
expect(option.unwrap()).toBe(true);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should return None on promise rejection with custom error', async () => {
|
|
426
|
+
const promise = Promise.reject(new Error("Custom error"));
|
|
427
|
+
const option = await option_from_promise(promise);
|
|
428
|
+
expect(option.is_none()).toBe(true);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
});
|