-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
TrySingle.cs
149 lines (140 loc) · 5.86 KB
/
TrySingle.cs
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
namespace SuperLinq.Async;
public static partial class AsyncSuperEnumerable
{
/// <summary>
/// Returns a tuple with the cardinality of the sequence and the
/// single element in the sequence if it contains exactly one element.
/// similar to <see cref="AsyncEnumerable.SingleAsync{TSource}(IAsyncEnumerable{TSource}, CancellationToken)"/>.
/// </summary>
/// <param name="source">The source sequence.</param>
/// <param name="zero">
/// The value that is returned in the tuple if the sequence has zero
/// elements.</param>
/// <param name="one">
/// The value that is returned in the tuple if the sequence has a
/// single element only.</param>
/// <param name="many">
/// The value that is returned in the tuple if the sequence has two or
/// more elements.</param>
/// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
/// <typeparam name="T">
/// The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TCardinality">
/// The type that expresses cardinality.</typeparam>
/// <returns>
/// A tuple containing the identified <typeparamref name="TCardinality"/>
/// and either the single value of <typeparamref name="T"/> in the sequence
/// or its default value.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null</exception>
/// <remarks>
/// This operator uses immediate execution, but never consumes more
/// than two elements from the sequence.
/// </remarks>
public static ValueTask<(TCardinality Cardinality, T? Value)>
TrySingle<T, TCardinality>(
this IAsyncEnumerable<T> source,
TCardinality zero, TCardinality one, TCardinality many,
CancellationToken cancellationToken = default)
{
return TrySingle(source, zero, one, many, ValueTuple.Create, cancellationToken);
}
/// <summary>
/// Returns a result projected from the the cardinality of the sequence
/// and the single element in the sequence if it contains exactly one
/// element.
/// </summary>
/// <param name="source">The source sequence.</param>
/// <param name="zero">
/// The value that is passed as the first argument to
/// <paramref name="resultSelector" /> if the sequence has zero
/// elements.</param>
/// <param name="one">
/// The value that is passed as the first argument to
/// <paramref name="resultSelector" /> if the sequence has a
/// single element only.</param>
/// <param name="many">
/// The value that is passed as the first argument to
/// <paramref name="resultSelector" /> if the sequence has two or
/// more elements.</param>
/// <param name="resultSelector">
/// A function that receives the cardinality and, if the
/// sequence has just one element, the value of that element as
/// argument and projects a resulting value of type
/// <typeparamref name="TResult"/>.</param>
/// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
/// <typeparam name="T">
/// The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TCardinality">
/// The type that expresses cardinality.</typeparam>
/// <typeparam name="TResult">
/// The type of the result value returned by the
/// <paramref name="resultSelector"/> function. </typeparam>
/// <returns>
/// The value returned by <paramref name="resultSelector"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="resultSelector"/> is null</exception>
/// <remarks>
/// This operator uses immediate execution, but never consumes more
/// than two elements from the sequence.
/// </remarks>
public static ValueTask<TResult> TrySingle<T, TCardinality, TResult>(
this IAsyncEnumerable<T> source,
TCardinality zero, TCardinality one, TCardinality many,
Func<TCardinality, T?, TResult> resultSelector,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(resultSelector);
return Core(source, zero, one, many, resultSelector, cancellationToken);
static async ValueTask<TResult> Core(
IAsyncEnumerable<T> source,
TCardinality zero, TCardinality one, TCardinality many,
Func<TCardinality, T?, TResult> resultSelector,
CancellationToken cancellationToken)
{
await using var e = source.GetConfiguredAsyncEnumerator(cancellationToken);
if (!await e.MoveNextAsync())
return resultSelector(zero, default);
var current = e.Current;
return !await e.MoveNextAsync()
? resultSelector(one, current)
: resultSelector(many, default);
}
}
/// <summary>
/// Returns the single element in the sequence if it contains exactly one element.
/// Similar to <see cref="AsyncEnumerable.SingleOrDefaultAsync{TSource}(IAsyncEnumerable{TSource}, CancellationToken)"/>.
/// </summary>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source"/>.
/// </typeparam>
/// <param name="source">
/// The source sequence.
/// </param>
/// <param name="cancellationToken">
/// The optional cancellation token to be used for cancelling the sequence at any time.
/// </param>
/// <returns>
/// The single element or the default value of <typeparamref name="TSource"/>.
/// </returns>
public static ValueTask<TSource?> TrySingle<TSource>(
this IAsyncEnumerable<TSource> source,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(source);
return Core(source, cancellationToken);
static async ValueTask<TSource?> Core(
IAsyncEnumerable<TSource> source,
CancellationToken cancellationToken)
{
await using var e = source.GetConfiguredAsyncEnumerator(cancellationToken);
if (!await e.MoveNextAsync())
return default;
var current = e.Current;
return !await e.MoveNextAsync()
? current
: default;
}
}
}