• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

KSP-CKAN / CKAN / 16123622788

07 Jul 2025 05:23PM UTC coverage: 47.823% (+0.02%) from 47.806%
16123622788

push

github

HebaruSan
Continue futile shadow war against AggregateException

3899 of 8735 branches covered (44.64%)

Branch coverage included in aggregate %.

1 of 2 new or added lines in 2 files covered. (50.0%)

8369 of 16918 relevant lines covered (49.47%)

1.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.75
/Core/Extensions/EnumerableExtensions.cs
1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.Collections.Concurrent;
5
using System.Linq;
6
using System.Threading;
7
using System.Threading.Tasks;
8
using System.Text.RegularExpressions;
9

10
namespace CKAN.Extensions
11
{
12
    public static class EnumerableExtensions
13
    {
14
        public static ConcurrentDictionary<K, V> ToConcurrentDictionary<K, V>(this IEnumerable<KeyValuePair<K, V>> pairs) where K: class
15
            => new ConcurrentDictionary<K, V>(pairs);
2✔
16

17
        public static IEnumerable<T> AsParallelIf<T>(this IEnumerable<T> source,
18
                                                     bool                parallel)
19
            => parallel ? source.AsParallel()
2✔
20
                        : source;
21

22
        // https://stackoverflow.com/a/55591477/2422988
23
        public static ParallelQuery<T> WithProgress<T>(this ParallelQuery<T> source,
24
                                                       long                  totalCount,
25
                                                       IProgress<int>?       progress)
26
        {
2✔
27
            long count       = 0;
2✔
28
            int  prevPercent = -1;
2✔
29
            return progress == null
2!
30
                ? source
31
                : source.Select(item =>
32
                {
×
33
                    var percent = (int)(100 * Interlocked.Increment(ref count) / totalCount);
×
34
                    if (percent > prevPercent)
×
35
                    {
×
36
                        progress.Report(percent);
×
37
                        prevPercent = percent;
×
38
                    }
×
39
                    return item;
×
40
                });
×
41
        }
2✔
42

43
        public static IEnumerable<T> Memoize<T>(this IEnumerable<T> source)
44
            => source is Memoized<T>
2!
45
                   // Already memoized, don't wrap another layer
46
                   ? source
47
                   : new Memoized<T>(source);
48

49
        public static void RemoveWhere<K, V>(this Dictionary<K, V> source,
50
                                             Func<KeyValuePair<K, V>, bool> predicate) where K: class
51
        {
2✔
52
            var pairs = source.ToList();
2✔
53
            foreach (var kvp in pairs)
5✔
54
            {
2✔
55
                if (predicate(kvp))
2✔
56
                {
2✔
57
                    source.Remove(kvp.Key);
2✔
58
                }
2✔
59
            }
2✔
60
        }
2✔
61

62
        /// <summary>
63
        /// Sum a sequence of TimeSpans.
64
        /// Mysteriously not defined standardly.
65
        /// </summary>
66
        /// <param name="source">Sequence of TimeSpans to sum</param>
67
        /// <returns>
68
        /// Sum of the TimeSpans
69
        /// </returns>
70
        public static TimeSpan Sum(this IEnumerable<TimeSpan> source)
71
            => source.Aggregate(TimeSpan.Zero,
×
72
                                (a, b) => a + b);
×
73

74
        /// <summary>
75
        /// Select : SelectMany :: Zip : ZipMany
76
        /// </summary>
77
        /// <param name="seq1">Sequence from which to get first values</param>
78
        /// <param name="seq2">Sequence from which to get second values</param>
79
        /// <param name="func">Function to transform a value from each input sequence into a sequence of multiple outputs</param>
80
        /// <returns>Flattened sequence of values from func applies to seq1 and seq2</returns>
81
        public static IEnumerable<V> ZipMany<T, U, V>(this IEnumerable<T> seq1, IEnumerable<U> seq2, Func<T, U, IEnumerable<V>> func)
82
            => seq1.Zip(seq2, func).SelectMany(seqs => seqs);
×
83

84
        /// <summary>
85
        /// Zip a sequence with a sequence generated from the first sequence using the given function
86
        /// </summary>
87
        /// <typeparam name="T">Source sequence type</typeparam>
88
        /// <typeparam name="V">Type of elements returned by func</typeparam>
89
        /// <param name="source">Source sequence</param>
90
        /// <param name="func">Function to generate values of second sequence given source</param>
91
        /// <returns>Sequence of tuples containing pairs from each sequence</returns>
92
        public static IEnumerable<(T First, V Second)> ZipBy<T, V>(this IEnumerable<T> source, Func<IEnumerable<T>, IEnumerable<V>> func)
93
            => source.ToArray() is T[] array
2!
94
                   ? array.Zip(func(array))
95
                   : Enumerable.Empty<(T First, V Second)>();
96

97
        /// <summary>
98
        /// Insert new elements between consecutive pairs of existing elements,
99
        /// preserving the original elements in order, and using null to
100
        /// represent the elements before the beginning and after the end.
101
        /// </summary>
102
        /// <param name="source">Sequence into which to inject</param>
103
        /// <param name="inBetween">Function to generate the new elements</param>
104
        /// <returns>Sequence with new elements in it</returns>
105
        public static IEnumerable<T> Inject<T>(this IEnumerable<T> source,
106
                                               Func<T?, T?, T>     inBetween)
107
            where T : class
108
        {
×
109
            using (var e = source.GetEnumerator())
×
110
            {
×
111
                if (e.MoveNext())
×
112
                {
×
113
                    yield return inBetween(null, e.Current);
×
114
                    yield return e.Current;
×
115
                    var prev = e.Current;
×
116
                    while (e.MoveNext())
×
117
                    {
×
118
                        yield return inBetween(prev, e.Current);
×
119
                        yield return e.Current;
×
120
                        prev = e.Current;
×
121
                    }
×
122
                    yield return inBetween(prev, null);
×
123
                }
×
124
                else
125
                {
×
126
                    yield return inBetween(null, null);
×
127
                }
×
128
            }
×
129
        }
×
130

131
        /// <summary>
132
        /// Poor man's PLINQ, a trivially parallelized SelectMany that
133
        /// runs one process per item in the source sequence.
134
        /// For short sequences and long-running functions,
135
        /// when you don't feel like fighting with Partitioner.Create
136
        /// over how many items should be in each partition.
137
        /// </summary>
138
        /// <param name="source">The sequence to process</param>
139
        /// <param name="func">The function to apply to each item in the sequence</param>
140
        /// <returns>Sequence of all values from the function</returns>
141
        public static IEnumerable<V> SelectManyTasks<T, V>(this ICollection<T>     source,
142
                                                           Func<T, IEnumerable<V>> func)
143
        {
×
144
            if (source.Count <= 1)
×
145
            {
×
146
                return source.SelectMany(func);
×
147
            }
148
            else
149
            {
×
150
                var tasks = source.Select(item => Task.Run(() => func(item).ToArray()))
×
151
                                  // Force non-lazy creation of tasks
152
                                  .ToArray();
153
                // Without this, later tasks don't finish if an earlier one throws
154
                Task.WaitAll(tasks);
×
NEW
155
                return Utilities.WithRethrowInner(() => tasks.SelectMany(task => task.Result));
×
156
            }
157
        }
×
158

159
        /// <summary>
160
        /// Generate a sequence from a linked list
161
        /// </summary>
162
        /// <param name="start">The first node</param>
163
        /// <param name="getNext">Function to go from one node to the next</param>
164
        /// <returns>All the nodes in the list as a sequence</returns>
165
        public static IEnumerable<T> TraverseNodes<T>(this T start, Func<T, T?> getNext)
166
            where T : class
167
        {
2✔
168
            for (T? t = start; t != null; t = Utilities.DefaultIfThrows(() => getNext(t)))
4✔
169
            {
2✔
170
                yield return t;
2✔
171
            }
2✔
172
        }
2✔
173

174
        /// <summary>
175
        /// Try matching a regex against a series of strings and return the Match objects
176
        /// </summary>
177
        /// <param name="source">Sequence of strings to scan</param>
178
        /// <param name="pattern">Pattern to match</param>
179
        /// <returns>Sequence of Match objects</returns>
180
        public static IEnumerable<Match> WithMatches(this IEnumerable<string> source, Regex pattern)
181
            => source.Select(val => pattern.TryMatch(val, out Match? match) ? match : null)
×
182
                     .OfType<Match>();
183

184
        /// <summary>
185
        /// Apply a function to a sequence and handle any exceptions that are thrown
186
        /// </summary>
187
        /// <typeparam name="TSrc">Type of source sequence</typeparam>
188
        /// <typeparam name="TDest">Type of destination sequence</typeparam>
189
        /// <param name="source">Source sequence</param>
190
        /// <param name="func">Function to apply to each item</param>
191
        /// <param name="onThrow">Function to call if there's an exception</param>
192
        /// <returns>Sequence of return values of given function</returns>
193
        public static IEnumerable<TDest?> SelectWithCatch<TSrc, TDest>(this IEnumerable<TSrc>       source,
194
                                                                      Func<TSrc, TDest>             func,
195
                                                                      Func<TSrc, Exception, TDest?> onThrow)
196
                where TDest : class
197
            => source.Select(item => Utilities.DefaultIfThrows(()  => func(item),
2✔
198
                                                               exc => onThrow(item, exc)));
2✔
199

200
        /// <summary>
201
        /// Get a hash code for a sequence with a variable number of elements
202
        /// </summary>
203
        /// <typeparam name="T">Type of the elements in the sequence</typeparam>
204
        /// <param name="source">The sequence</param>
205
        /// <returns></returns>
206
        public static int ToSequenceHashCode<T>(this IEnumerable<T> source)
207
            => source.Aggregate(new HashCode(),
×
208
                                (hc, item) =>
209
                                {
×
210
                                    hc.Add(item);
×
211
                                    return hc;
×
212
                                },
×
213
                                hc => hc.ToHashCode());
×
214

215
        /// <summary>
216
        /// Accumulate a sequence of values based on a seed value and a function,
217
        /// similar to Aggregate but including intermediate values
218
        /// </summary>
219
        /// <typeparam name="TSource"></typeparam>
220
        /// <typeparam name="TResult"></typeparam>
221
        /// <param name="source">Input sequence</param>
222
        /// <param name="seed">First intermediate value, not included in return sequence</param>
223
        /// <param name="func">Function to transform a previous result and a next input sequence element into the next result</param>
224
        /// <returns></returns>
225
        public static IEnumerable<TResult> Accumulate<TSource, TResult>(this IEnumerable<TSource>       source,
226
                                                                        TResult                         seed,
227
                                                                        Func<TResult, TSource, TResult> func)
228
        {
×
229
            var result = seed;
×
230
            foreach (var item in source)
×
231
            {
×
232
                result = func(result, item);
×
233
                yield return result;
×
234
            }
×
235
        }
×
236
    }
237

238
    /// <summary>
239
    /// Memoized lazy evaluation in C#!
240
    /// From https://stackoverflow.com/a/12428250/2422988
241
    /// </summary>
242
    public class Memoized<T> : IEnumerable<T>
243
    {
244
        public Memoized(IEnumerable<T> source)
2✔
245
        {
2✔
246
            this.source = source;
2✔
247
        }
2✔
248

249
        IEnumerator IEnumerable.GetEnumerator()
250
            => GetEnumerator();
×
251

252
        public IEnumerator<T> GetEnumerator()
253
        {
2✔
254
            lock (gate)
2✔
255
            {
2✔
256
                if (isCacheComplete)
2✔
257
                {
2✔
258
                    return cache.GetEnumerator();
2✔
259
                }
260
                else
261
                {
2✔
262
                    enumerator ??= source.GetEnumerator();
2!
263
                }
2✔
264
            }
2✔
265
            return GetMemoizingEnumerator();
2✔
266
        }
2✔
267

268
        private IEnumerator<T> GetMemoizingEnumerator()
269
        {
2✔
270
            for (int index = 0; TryGetItem(index, out T? item); ++index)
4✔
271
            {
2✔
272
                yield return item;
2✔
273
            }
2✔
274
        }
2✔
275

276
        private bool TryGetItem(int index,
277
                                out T item)
278
        {
2✔
279
            lock (gate)
2✔
280
            {
2✔
281
                if (enumerator is not null && !IsItemInCache(index))
2!
282
                {
2✔
283
                    // The iteration may have completed while waiting for the lock
284
                    #nullable disable
285
                    if (isCacheComplete)
2!
286
                    {
×
287
                        item = default;
×
288
                        return false;
×
289
                    }
290
                    if (!enumerator.MoveNext())
2✔
291
                    {
2✔
292
                        item = default;
2✔
293
                        isCacheComplete = true;
2✔
294
                        enumerator.Dispose();
2✔
295
                        return false;
2✔
296
                    }
297
                    #nullable enable
298
                    cache.Add(enumerator.Current);
2✔
299
                }
2✔
300
                item = cache[index];
2✔
301
                return true;
2✔
302
            }
303
        }
2✔
304

305
        private bool IsItemInCache(int index)
306
            => index < cache.Count;
2✔
307

308
        private readonly IEnumerable<T>  source;
309
        private          IEnumerator<T>? enumerator;
310
        private readonly List<T>         cache = new List<T>();
2✔
311
        private          bool            isCacheComplete;
312
        private readonly object          gate = new object();
2✔
313
    }
314
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc