@@ -4,28 +4,28 @@ export type ArrayResultOptions = {
44 cause : unknown ;
55 isHtml : boolean ;
66} ;
7- export class ArrayResultError extends Error {
7+ export class ResultError extends Error {
88 isHtml : boolean ;
99 override name = "ArrayResultError" ;
1010 constructor ( message : string , options : Partial < ArrayResultOptions > = { } ) {
1111 super ( message , { cause : options . cause } ) ;
1212 this . isHtml = options . isHtml ?? false ;
1313 }
1414}
15- export class TodoError extends ArrayResultError {
15+ export class TodoError extends ResultError {
1616 override name = "TodoError" ;
1717 constructor ( functionality : string ) {
1818 super ( `${ functionality } is not yet implemented` ) ;
1919 }
2020}
2121export class ArrayResult < const T > {
2222 constructor ( array ?: ReadonlyArray < T > ) ;
23- constructor ( array : undefined , errors : ReadonlyArray < ArrayResultError > ) ;
23+ constructor ( array : undefined , errors : ReadonlyArray < ResultError > ) ;
2424 constructor (
2525 public readonly array : ReadonlyArray < T > = [ ] ,
26- public readonly errors : ReadonlyArray < ArrayResultError > = [ ] ,
26+ public readonly errors : ReadonlyArray < ResultError > = [ ] ,
2727 ) { }
28- static errors ( errors : ReadonlyArray < ArrayResultError > ) : ArrayResult < never > {
28+ static errors ( errors : ReadonlyArray < ResultError > ) : ArrayResult < never > {
2929 return new ArrayResult ( undefined , errors ) ;
3030 }
3131 static empty ( ) : ArrayResult < never > {
@@ -75,13 +75,20 @@ export class ArrayResult<const T> {
7575 sortBy ( mapper : ( value : T ) => number ) : ArrayResult < T > {
7676 return this . sort ( ( left , right ) => mapper ( left ) - mapper ( right ) ) ;
7777 }
78- addErrorWhenNone ( error : ( ) => ArrayResultError ) : ArrayResult < T > {
78+ addErrorWhenNone ( error : ( ) => ResultError ) : ArrayResult < T > {
7979 if ( this . isError ( ) && this . errors . length === 0 ) {
8080 return ArrayResult . errors ( [ error ( ) ] ) ;
8181 } else {
8282 return this ;
8383 }
8484 }
85+ asIterableResult ( ) : IterableResult < T > {
86+ if ( this . isError ( ) ) {
87+ return IterableResult . errors ( this . errors ) ;
88+ } else {
89+ return IterableResult . fromArray ( this . unwrap ( ) ) ;
90+ }
91+ }
8592 static concat < T > (
8693 ...arrayResults : ReadonlyArray < ArrayResult < T > >
8794 ) : ArrayResult < T > {
@@ -118,20 +125,204 @@ export class ArrayResult<const T> {
118125 try {
119126 return arrayResult ( ) ;
120127 } 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 ) ) ;
122313 }
123314 }
124315}
125- export function extractArrayResultError (
316+ export function extractResultError (
126317 error : unknown ,
127- ) : ReadonlyArray < ArrayResultError > {
128- if ( error instanceof ArrayResultError ) {
318+ ) : ReadonlyArray < ResultError > {
319+ if ( error instanceof ResultError ) {
129320 return [ error ] ;
130321 } else if ( error instanceof AggregateError ) {
131322 const { errors } = error ;
132323 if (
133324 errors . length > 0 &&
134- errors . every ( ( error ) => error instanceof ArrayResultError )
325+ errors . every ( ( error ) => error instanceof ResultError )
135326 ) {
136327 return errors ;
137328 }
0 commit comments