Skip to content

Commit 4b6dbc3

Browse files
committed
Impl traverseTaskResultM' traverseTaskResultM
1 parent 44b317f commit 4b6dbc3

File tree

2 files changed

+90
-1
lines changed
  • src/FsToolkit.ErrorHandling
  • tests/FsToolkit.ErrorHandling.Tests

2 files changed

+90
-1
lines changed

src/FsToolkit.ErrorHandling/Seq.fs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,51 @@ let traverseAsyncResultM f xs =
167167
/// <remarks>This function is equivalent to <see cref="traverseAsyncResultM"/> but auto-applying the 'id' function</remarks>
168168
let sequenceAsyncResultM xs = traverseAsyncResultM id xs
169169

170+
/// <summary>
171+
/// Applies a function to each element of a sequence and returns a single Task result
172+
/// </summary>
173+
/// <param name="state">The initial state</param>
174+
/// <param name="f">The function to apply to each element</param>
175+
/// <param name="xs">The input sequence</param>
176+
/// <returns>A task result with the ok elements in an array or the first error encountered in the state or the sequence</returns>
177+
let inline traverseTaskResultM'
178+
(state: TaskResult<'okOutput seq, 'error>)
179+
([<InlineIfLambda>] f: 'okInput -> TaskResult<'okOutput, 'error>)
180+
(xs: 'okInput seq)
181+
: TaskResult<'okOutput[], 'error> =
182+
if isNull xs then
183+
nullArg (nameof xs)
184+
185+
task {
186+
match! state with
187+
| Error e -> return Error e
188+
| Ok initialSuccesses ->
189+
let oks = ResizeArray(initialSuccesses)
190+
let mutable ok = true
191+
let mutable err = Unchecked.defaultof<'error>
192+
use e = xs.GetEnumerator()
193+
194+
while ok
195+
&& e.MoveNext() do
196+
match! f e.Current with
197+
| Ok r -> oks.Add r
198+
| Error e ->
199+
ok <- false
200+
err <- e
201+
202+
return if ok then Ok(oks.ToArray()) else Error err
203+
}
204+
205+
/// <summary>
206+
/// Applies a function to each element of a sequence and returns a single Task result
207+
/// </summary>
208+
/// <param name="f">The function to apply to each element</param>
209+
/// <param name="xs">The input sequence</param>
210+
/// <returns>A task result with the ok elements in an array or the first error occurring in the sequence</returns>
211+
/// <remarks>This function is equivalent to <see cref="traverseTaskResultM'"/> but applying an initial state of 'Seq.empty'</remarks>
212+
let traverseTaskResultM f xs =
213+
traverseTaskResultM' (TaskResult.ok Seq.empty) f xs
214+
170215
/// <summary>
171216
/// Applies a function to each element of a sequence and returns a single async result
172217
/// </summary>

tests/FsToolkit.ErrorHandling.Tests/Seq.fs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,6 @@ let traverseAsyncResultATests =
470470
}
471471
]
472472

473-
474473
let sequenceAsyncResultMTests =
475474
let userIds =
476475
seq {
@@ -512,6 +511,50 @@ let sequenceAsyncResultMTests =
512511
}
513512
]
514513

514+
let traverseTaskResultATests =
515+
let userIds =
516+
seq {
517+
userId1
518+
userId2
519+
userId3
520+
userId4
521+
}
522+
|> Seq.map UserId
523+
524+
testList "Seq.traverseTaskResultA Tests" [
525+
testCaseAsync "traverseTaskResultA with a sequence of valid data"
526+
<| async {
527+
let expected =
528+
userIds
529+
|> Seq.map (fun (UserId user) -> (newPostId, user))
530+
|> Seq.toList
531+
532+
let! actual = Seq.traverseTaskResultA (notifyNewPostSuccess (PostId newPostId)) userIds
533+
534+
let actual =
535+
Expect.wantOk actual "Expected result to be Ok"
536+
|> Seq.toList
537+
538+
Expect.equal actual expected "Should have a sequence of valid data"
539+
}
540+
541+
testCaseAsync "traverseResultA with few invalid data"
542+
<| async {
543+
let expected = [
544+
sprintf "error: %s" (userId1.ToString())
545+
sprintf "error: %s" (userId3.ToString())
546+
]
547+
548+
let! actual = Seq.traverseTaskResultA (notifyFailure (PostId newPostId)) userIds
549+
550+
let actual =
551+
Expect.wantError actual "Expected result to be Error"
552+
|> Seq.toList
553+
554+
Expect.equal actual expected "Should have a sequence of errors"
555+
}
556+
]
557+
515558
let sequenceAsyncOptionMTests =
516559

517560
let userIds =
@@ -723,6 +766,7 @@ let allTests =
723766
traverseAsyncResultMTests
724767
traverseAsyncOptionMTests
725768
traverseAsyncResultATests
769+
traverseTaskResultATests
726770
sequenceAsyncResultMTests
727771
sequenceAsyncOptionMTests
728772
sequenceAsyncResultATests

0 commit comments

Comments
 (0)