@@ -4,28 +4,28 @@ export type ArrayResultOptions = {
4
4
cause : unknown ;
5
5
isHtml : boolean ;
6
6
} ;
7
- export class ArrayResultError extends Error {
7
+ export class ResultError extends Error {
8
8
isHtml : boolean ;
9
9
override name = "ArrayResultError" ;
10
10
constructor ( message : string , options : Partial < ArrayResultOptions > = { } ) {
11
11
super ( message , { cause : options . cause } ) ;
12
12
this . isHtml = options . isHtml ?? false ;
13
13
}
14
14
}
15
- export class TodoError extends ArrayResultError {
15
+ export class TodoError extends ResultError {
16
16
override name = "TodoError" ;
17
17
constructor ( functionality : string ) {
18
18
super ( `${ functionality } is not yet implemented` ) ;
19
19
}
20
20
}
21
21
export class ArrayResult < const T > {
22
22
constructor ( array ?: ReadonlyArray < T > ) ;
23
- constructor ( array : undefined , errors : ReadonlyArray < ArrayResultError > ) ;
23
+ constructor ( array : undefined , errors : ReadonlyArray < ResultError > ) ;
24
24
constructor (
25
25
public readonly array : ReadonlyArray < T > = [ ] ,
26
- public readonly errors : ReadonlyArray < ArrayResultError > = [ ] ,
26
+ public readonly errors : ReadonlyArray < ResultError > = [ ] ,
27
27
) { }
28
- static errors ( errors : ReadonlyArray < ArrayResultError > ) : ArrayResult < never > {
28
+ static errors ( errors : ReadonlyArray < ResultError > ) : ArrayResult < never > {
29
29
return new ArrayResult ( undefined , errors ) ;
30
30
}
31
31
static empty ( ) : ArrayResult < never > {
@@ -75,13 +75,20 @@ export class ArrayResult<const T> {
75
75
sortBy ( mapper : ( value : T ) => number ) : ArrayResult < T > {
76
76
return this . sort ( ( left , right ) => mapper ( left ) - mapper ( right ) ) ;
77
77
}
78
- addErrorWhenNone ( error : ( ) => ArrayResultError ) : ArrayResult < T > {
78
+ addErrorWhenNone ( error : ( ) => ResultError ) : ArrayResult < T > {
79
79
if ( this . isError ( ) && this . errors . length === 0 ) {
80
80
return ArrayResult . errors ( [ error ( ) ] ) ;
81
81
} else {
82
82
return this ;
83
83
}
84
84
}
85
+ asIterableResult ( ) : IterableResult < T > {
86
+ if ( this . isError ( ) ) {
87
+ return IterableResult . errors ( this . errors ) ;
88
+ } else {
89
+ return IterableResult . fromArray ( this . unwrap ( ) ) ;
90
+ }
91
+ }
85
92
static concat < T > (
86
93
...arrayResults : ReadonlyArray < ArrayResult < T > >
87
94
) : ArrayResult < T > {
@@ -118,20 +125,204 @@ export class ArrayResult<const T> {
118
125
try {
119
126
return arrayResult ( ) ;
120
127
} catch ( error ) {
121
- return ArrayResult . errors ( extractArrayResultError ( error ) ) ;
128
+ return ArrayResult . errors ( extractResultError ( error ) ) ;
129
+ }
130
+ }
131
+ }
132
+ export type Result < T > =
133
+ | Readonly < { type : "value" ; value : T } >
134
+ | Readonly < { type : "error" ; error : ResultError } > ;
135
+
136
+ export class IterableResult < const T > {
137
+ constructor ( public readonly iterable : ( ) => Generator < Result < T > > ) { }
138
+ static fromArray < const T > ( array : ReadonlyArray < T > ) : IterableResult < T > {
139
+ return new IterableResult ( function * ( ) {
140
+ for ( const value of array ) {
141
+ yield { type : "value" , value } ;
142
+ }
143
+ } ) ;
144
+ }
145
+ static single < const T > ( value : T ) : IterableResult < T > {
146
+ return new IterableResult ( function * ( ) {
147
+ yield { type : "value" , value } ;
148
+ } ) ;
149
+ }
150
+ static errors ( errors : ReadonlyArray < ResultError > ) : IterableResult < never > {
151
+ return new IterableResult ( function * ( ) {
152
+ for ( const error of errors ) {
153
+ yield { type : "error" , error } ;
154
+ }
155
+ } ) ;
156
+ }
157
+ static empty ( ) : IterableResult < never > {
158
+ return new IterableResult ( function * ( ) { } ) ;
159
+ }
160
+ filter ( mapper : ( value : T ) => boolean ) : IterableResult < T > {
161
+ return this . flatMap ( ( value ) =>
162
+ mapper ( value ) ? IterableResult . single ( value ) : IterableResult . empty ( )
163
+ ) ;
164
+ }
165
+ map < const U > ( mapper : ( value : T ) => U ) : IterableResult < U > {
166
+ return this . flatMap ( ( value ) => IterableResult . single ( mapper ( value ) ) ) ;
167
+ }
168
+ flatMap < const U > ( mapper : ( value : T ) => IterableResult < U > ) : IterableResult < U > {
169
+ const iterable = this . iterable ;
170
+ return new IterableResult ( function * ( ) {
171
+ const errors : Array < ResultError > = [ ] ;
172
+ let yielded = false ;
173
+ for ( const result of iterable ( ) ) {
174
+ switch ( result . type ) {
175
+ case "value" : {
176
+ const more = IterableResult . from ( ( ) => mapper ( result . value ) ) ;
177
+ for ( const result of more . iterable ( ) ) {
178
+ switch ( result . type ) {
179
+ case "value" :
180
+ yielded = false ;
181
+ yield result ;
182
+ break ;
183
+ case "error" :
184
+ errors . push ( result . error ) ;
185
+ }
186
+ }
187
+ break ;
188
+ }
189
+ case "error" :
190
+ yield result ;
191
+ }
192
+ }
193
+ if ( ! yielded ) {
194
+ for ( const error of errors ) {
195
+ yield { type : "error" , error } as const ;
196
+ }
197
+ }
198
+ } ) ;
199
+ }
200
+ filterMap < const U > ( mapper : ( value : T ) => U ) : IterableResult < NonNullable < U > > {
201
+ return this . flatMap ( ( value ) => {
202
+ const newValue = mapper ( value ) ;
203
+ if ( newValue == null ) {
204
+ return IterableResult . empty ( ) ;
205
+ } else {
206
+ return IterableResult . single ( newValue ) ;
207
+ }
208
+ } ) ;
209
+ }
210
+ addErrorWhenNone ( error : ( ) => ResultError ) : IterableResult < T > {
211
+ const iterable = this . iterable ;
212
+ return new IterableResult ( function * ( ) {
213
+ let yielded = false ;
214
+ for ( const result of iterable ( ) ) {
215
+ yielded = true ;
216
+ yield result ;
217
+ }
218
+ if ( ! yielded ) {
219
+ yield { type : "error" , error : error ( ) } ;
220
+ }
221
+ } ) ;
222
+ }
223
+ static concat < T > (
224
+ ...iterableResults : ReadonlyArray < IterableResult < T > >
225
+ ) : IterableResult < T > {
226
+ return new IterableResult ( function * ( ) {
227
+ const errors : Array < ResultError > = [ ] ;
228
+ let yielded = false ;
229
+ for ( const iterable of iterableResults ) {
230
+ for ( const result of iterable . iterable ( ) ) {
231
+ switch ( result . type ) {
232
+ case "value" :
233
+ yielded = true ;
234
+ yield result ;
235
+ break ;
236
+ case "error" :
237
+ errors . push ( result . error ) ;
238
+ break ;
239
+ }
240
+ }
241
+ }
242
+ if ( ! yielded ) {
243
+ for ( const error of errors ) {
244
+ yield { type : "error" , error } as const ;
245
+ }
246
+ }
247
+ } ) ;
248
+ }
249
+ static combine < T extends ReadonlyArray < unknown > > (
250
+ ...iterableResults :
251
+ & Readonly < { [ I in keyof T ] : IterableResult < T [ I ] > } >
252
+ & Readonly < { length : T [ "length" ] } >
253
+ ) : IterableResult < T > {
254
+ // we resorted to using `any` types here, make sure it works properly
255
+ return iterableResults . reduce (
256
+ ( left : IterableResult < any > , right ) =>
257
+ new IterableResult ( function * ( ) {
258
+ let rightAggregate : null | Array < any > = null ;
259
+ let yielded = false ;
260
+ for ( const leftResult of left . iterable ( ) ) {
261
+ switch ( leftResult . type ) {
262
+ case "value" :
263
+ if ( rightAggregate == null ) {
264
+ rightAggregate = [ ] ;
265
+ for ( const rightResult of right . iterable ( ) ) {
266
+ switch ( rightResult . type ) {
267
+ case "value" : {
268
+ const right = rightResult . value ;
269
+ rightAggregate . push ( right ) ;
270
+ yielded = true ;
271
+ yield {
272
+ type : "value" ,
273
+ value : [ ...leftResult . value , right ] ,
274
+ } ;
275
+ break ;
276
+ }
277
+ case "error" :
278
+ yield rightResult ;
279
+ break ;
280
+ }
281
+ }
282
+ } else {
283
+ for ( const right of rightAggregate ) {
284
+ yielded = true ;
285
+ yield {
286
+ type : "value" ,
287
+ value : [ ...leftResult . value , right ] ,
288
+ } ;
289
+ }
290
+ }
291
+ break ;
292
+ case "error" :
293
+ yield leftResult ;
294
+ break ;
295
+ }
296
+ }
297
+ if ( ! yielded && rightAggregate == null ) {
298
+ for ( const result of right . iterable ( ) ) {
299
+ if ( result . type === "error" ) {
300
+ yield result ;
301
+ }
302
+ }
303
+ }
304
+ } ) ,
305
+ IterableResult . single ( [ ] ) as IterableResult < any > ,
306
+ ) as IterableResult < T > ;
307
+ }
308
+ static from < T > ( iterableResult : ( ) => IterableResult < T > ) : IterableResult < T > {
309
+ try {
310
+ return iterableResult ( ) ;
311
+ } catch ( error ) {
312
+ return IterableResult . errors ( extractResultError ( error ) ) ;
122
313
}
123
314
}
124
315
}
125
- export function extractArrayResultError (
316
+ export function extractResultError (
126
317
error : unknown ,
127
- ) : ReadonlyArray < ArrayResultError > {
128
- if ( error instanceof ArrayResultError ) {
318
+ ) : ReadonlyArray < ResultError > {
319
+ if ( error instanceof ResultError ) {
129
320
return [ error ] ;
130
321
} else if ( error instanceof AggregateError ) {
131
322
const { errors } = error ;
132
323
if (
133
324
errors . length > 0 &&
134
- errors . every ( ( error ) => error instanceof ArrayResultError )
325
+ errors . every ( ( error ) => error instanceof ResultError )
135
326
) {
136
327
return errors ;
137
328
}
0 commit comments