Summary

Class:AsyncEnumeration.Implementation.Enumerable.SequentialCurrentInfoFactory
Assembly:AsyncEnumeration.Implementation.Enumerable
File(s):/repo-dir/contents/Source/Code/AsyncEnumeration.Implementation.Enumerable/Factory.cs
Covered lines:10
Uncovered lines:0
Coverable lines:10
Total lines:372
Line coverage:100%
Branch coverage:100%
Tag:7d9974899246b95481b7aa9cd3a1462ae2a67c91

Coverage History

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.cctor()101%0%
GetFactory(...)101%0%
GetInstance(...)401%1%

File(s)

/repo-dir/contents/Source/Code/AsyncEnumeration.Implementation.Enumerable/Factory.cs

#LineLine coverage
 1/*
 2 * Copyright 2017 Stanislav Muhametsin. All rights Reserved.
 3 *
 4 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 5 * you may not use  this file  except in  compliance with the License.
 6 * You may obtain a copy of the License at
 7 *
 8 *   http://www.apache.org/licenses/LICENSE-2.0
 9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed  under the  License is distributed on an "AS IS" BASIS,
 12 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 13 * implied.
 14 *
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18using AsyncEnumeration.Abstractions;
 19using System;
 20using System.Collections.Generic;
 21using System.Text;
 22using System.Threading;
 23using System.Threading.Tasks;
 24using UtilPack;
 25using TSequentialCurrentInfoFactory = System.Func<System.Object, AsyncEnumeration.Implementation.Enumerable.EnumerationE
 26
 27namespace AsyncEnumeration.Implementation.Enumerable
 28{
 29   /// <summary>
 30   /// This class provides static factory methods to create objects of type <see cref="IAsyncEnumerator{T}"/>.
 31   /// </summary>
 32   public static class AsyncEnumerationFactory
 33   {
 34      /// <summary>
 35      /// Creates a new <see cref="IAsyncEnumerable{T}"/> which will use the given callback as implementation for <see c
 36      /// </summary>
 37      /// <typeparam name="T">The type of items being iterated.</typeparam>
 38      /// <param name="getEnumerator">The callback for <see cref="IAsyncEnumerable{T}.GetAsyncEnumerator"/> implementati
 39      /// <param name="asyncProvider">The <see cref="IAsyncProvider"/> to use.</param>
 40      /// <returns>A new instance of <see cref="IAsyncEnumerable{T}"/> with given parameters.</returns>
 41      /// <exception cref="ArgumentNullException">If either of <paramref name="getEnumerator"/> or <paramref name="async
 42      public static IAsyncEnumerable<T> FromGeneratorCallback<T>(
 43         Func<IAsyncEnumerator<T>> getEnumerator,
 44         IAsyncProvider asyncProvider
 45         )
 46      {
 47         return new AsyncEnumerableFunctionalWrapper<T>( asyncProvider, getEnumerator );
 48      }
 49
 50      /// <summary>
 51      /// Creates a new <see cref="IAsyncEnumerable{T}"/> which will use the given callback as implementation for <see c
 52      /// </summary>
 53      /// <typeparam name="T">The type of items being iterated.</typeparam>
 54      /// <typeparam name="TArg">The type of argument for the callback.</typeparam>
 55      /// <param name="arg">The argument for the <paramref name="getEnumerator"/> callback.</param>
 56      /// <param name="getEnumerator">The callback for <see cref="IAsyncEnumerable{T}.GetAsyncEnumerator"/> implementati
 57      /// <param name="asyncProvider">The <see cref="IAsyncProvider"/> to use.</param>
 58      /// <returns>A new instance of <see cref="IAsyncEnumerable{T}"/> with given parameters.</returns>
 59      /// <exception cref="ArgumentNullException">If either of <paramref name="getEnumerator"/> or <paramref name="async
 60      public static IAsyncEnumerable<T> FromGeneratorCallback<T, TArg>(
 61         TArg arg,
 62         Func<TArg, IAsyncEnumerator<T>> getEnumerator,
 63         IAsyncProvider asyncProvider
 64         )
 65      {
 66         return new AsyncEnumerableFunctionalWrapper<T, TArg>( asyncProvider, arg, getEnumerator );
 67      }
 68
 69      /// <summary>
 70      /// Creates a new instance of <see cref="IAsyncEnumerable{T}"/> with given callback to create <see cref="Sequentia
 71      /// </summary>
 72      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 73      /// <param name="enumerationStart">The callback to create a new <see cref="SequentialEnumerationStartInfo{T}"/> fo
 74      /// <param name="asyncProvider">The <see cref="IAsyncProvider"/> for the returned <see cref="IAsyncEnumerable{T}"/
 75      /// <returns>A new instance of <see cref="IAsyncEnumerable{T}"/> which behaves like <see cref="SequentialEnumerati
 76      /// <remarks>
 77      /// If <paramref name="enumerationStart"/> is <c>null</c>, then result is empty enumerable.
 78      /// </remarks>
 79      /// <seealso cref="CreateSequentialStartInfo{T}(MoveNextAsyncDelegate{T}, EnumerationEndedDelegate)"/>
 80      public static IAsyncEnumerable<T> CreateSequentialEnumerable<T>(
 81         Func<SequentialEnumerationStartInfo<T>> enumerationStart,
 82         IAsyncProvider asyncProvider
 83         ) => enumerationStart == null ? EmptyAsync<T>.Enumerable : new AsyncSequentialOnlyEnumerable<T>( enumerationSta
 84
 85      /// <summary>
 86      /// Creates a new <see cref="IAsyncEnumerable{T}"/> which will allow at most one <see cref="IAsyncEnumerator{T}"/>
 87      /// </summary>
 88      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 89      /// <param name="startInfo">The <see cref="SequentialEnumerationStartInfo{T}"/> containing callbacks to use.</para
 90      /// <param name="asyncProvider">The <see cref="IAsyncProvider"/> for the returned <see cref="IAsyncEnumerable{T}"/
 91      /// <returns>A new instance of <see cref="IAsyncEnumerable{T}"/> which behaves like callbacks in <paramref name="s
 92      public static IAsyncEnumerable<T> CreateExclusiveSequentialEnumerable<T>(
 93         SequentialEnumerationStartInfo<T> startInfo,
 94         IAsyncProvider asyncProvider
 95         ) => new AsyncEnumerableExclusive<T>( SequentialCurrentInfoFactory.GetInstance( startInfo.MoveNext, startInfo.D
 96
 97      /// <summary>
 98      /// Helper method to invoke constructor of <see cref="SequentialEnumerationStartInfo{T}"/> without explicitly spec
 99      /// </summary>
 100      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 101      /// <param name="moveNext">The callback for potentially asynchronously fetching next item.</param>
 102      /// <param name="dispose">The callback to dispose enumerator.</param>
 103      /// <returns>A new <see cref="SequentialEnumerationStartInfo{T}"/>.</returns>
 104      /// <seealso cref="SequentialEnumerationStartInfo{T}.SequentialEnumerationStartInfo(MoveNextAsyncDelegate{T}, Enum
 105      public static SequentialEnumerationStartInfo<T> CreateSequentialStartInfo<T>(
 106         MoveNextAsyncDelegate<T> moveNext,
 107         EnumerationEndedDelegate dispose
 108         ) => new SequentialEnumerationStartInfo<T>( moveNext, dispose );
 109
 110      /// <summary>
 111      /// Creates a new instance of <see cref="IAsyncEnumerator{T}"/> which fetches one item at a time using given callb
 112      /// </summary>
 113      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 114      /// <param name="moveNext">The callback for potentially asynchronously fetching next item.</param>
 115      /// <param name="dispose">The callback to dispose enumerator.</param>
 116      /// <returns>A new instance of <see cref="IAsyncEnumerator{T}"/> which behaves like <paramref name="moveNext"/> an
 117      /// <remarks>
 118      /// The returned <see cref="IAsyncEnumerator{T}"/> will have guard code to prevent concurrent invocation.
 119      /// </remarks>
 120      public static IAsyncEnumerator<T> CreateSequentialEnumerator<T>(
 121         MoveNextAsyncDelegate<T> moveNext,
 122         EnumerationEndedDelegate dispose
 123         ) => new AsyncEnumerator<T>( SequentialCurrentInfoFactory.GetInstance( moveNext, dispose ) );
 124
 125      /// <summary>
 126      /// This creates a <see cref="IAsyncEnumerable{T}"/> that will behave like the given set of delegates.
 127      /// Each enumeration is assumed to have inner state, meaning that the call to create delegates is done on every ca
 128      /// </summary>
 129      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 130      /// <param name="startInfoFactory">The callback creating a structure containing all the delegates that the calls t
 131      /// <param name="asyncProvider">The <see cref="IAsyncProvider"/> for the returned <see cref="IAsyncEnumerable{T}"/
 132      /// <returns>A new <see cref="IAsyncEnumerable{T}"/>, which will call the given <paramref name="startInfoFactory"/
 133      /// <exception cref="ArgumentNullException">If <paramref name="startInfoFactory"/> is <c>null</c>.</exception>
 134      /// <seealso cref="WrappingEnumerationStartInfo{T}"/>
 135      /// <seealso cref="CreateWrappingStartInfo"/>
 136      public static IAsyncEnumerable<T> CreateStatefulWrappingEnumerable<T>(
 137         Func<WrappingEnumerationStartInfo<T>> startInfoFactory,
 138         IAsyncProvider asyncProvider
 139         )
 140      {
 141         return new StatefulAsyncEnumerableWrapper<T>( startInfoFactory, asyncProvider );
 142      }
 143
 144      /// <summary>
 145      /// This creates a <see cref="IAsyncEnumerable{T}"/> that will behave like the given set of delegates.
 146      /// Each enumeration is assumed to be stateless, meaning that the same set of delegates is shared by all instances
 147      /// </summary>
 148      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 149      /// <param name="startInfo">The <see cref="WrappingEnumerationStartInfo{T}"/> containing delegates that capture al
 150      /// <returns>A new <see cref="IAsyncEnumerable{T}"/>, which will share the given delegates by all instances return
 151      /// <param name="asyncProvider">The <see cref="IAsyncProvider"/> for the returned <see cref="IAsyncEnumerable{T}"/
 152      /// <exception cref="ArgumentNullException">If either of <see cref="WrappingEnumerationStartInfo{T}.WaitForNext"/>
 153      /// <seealso cref="WrappingEnumerationStartInfo{T}"/>
 154      /// <seealso cref="CreateWrappingStartInfo"/>
 155      public static IAsyncEnumerable<T> CreateStatelessWrappingEnumerable<T>(
 156         WrappingEnumerationStartInfo<T> startInfo,
 157         IAsyncProvider asyncProvider
 158         )
 159      {
 160         return new StatelessAsyncEnumerableWrapper<T>( startInfo, asyncProvider );
 161      }
 162
 163      /// <summary>
 164      /// This creates a <see cref="IAsyncEnumerator{T}"/> that will behave like the given set of delegates.
 165      /// </summary>
 166      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 167      /// <param name="startInfo">The <see cref="WrappingEnumerationStartInfo{T}"/> containing delegates that capture al
 168      /// <returns>A new <see cref="IAsyncEnumerator{T}"/>, which will behave like the given set of delegates.</returns>
 169      /// <exception cref="ArgumentNullException">If either of <see cref="WrappingEnumerationStartInfo{T}.WaitForNext"/>
 170      /// <seealso cref="WrappingEnumerationStartInfo{T}"/>
 171      /// <seealso cref="CreateWrappingStartInfo"/>
 172      public static IAsyncEnumerator<T> CreateWrappingEnumerator<T>(
 173         WrappingEnumerationStartInfo<T> startInfo
 174         )
 175      {
 176         return new AsyncEnumeratorWrapper<T>( startInfo );
 177      }
 178
 179      /// <summary>
 180      ///  Helper method to invoke constructor of <see cref="WrappingEnumerationStartInfo{T}"/> without explicitly speci
 181      /// </summary>
 182      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 183      /// <param name="waitForNext">The callback that will be used by <see cref="IAsyncEnumerator{T}.WaitForNextAsync"/>
 184      /// <param name="tryGetNext">The callback that will be used by <see cref="IAsyncEnumerator{T}.TryGetNext"/>.</para
 185      /// <param name="dispose">The optional callback that will be used by <see cref="IAsyncDisposable.DisposeAsync"/>.<
 186      /// <returns>A new instance of <see cref="WrappingEnumerationStartInfo{T}"/>.</returns>
 187      /// <exception cref="ArgumentNullException">If either of <paramref name="waitForNext"/> or <paramref name="tryGetN
 188      /// <seealso cref="WrappingEnumerationStartInfo{T}"/>
 189      public static WrappingEnumerationStartInfo<T> CreateWrappingStartInfo<T>(
 190         WaitForNextDelegate waitForNext,
 191         TryGetNextDelegate<T> tryGetNext,
 192         EnumerationEndedDelegate dispose
 193         )
 194      {
 195         return new WrappingEnumerationStartInfo<T>( waitForNext, tryGetNext, dispose );
 196      }
 197
 198      /// <summary>
 199      /// Helper method to create <see cref="WrappingEnumerationStartInfo{T}"/> which will return <c>true</c> only once 
 200      /// </summary>
 201      /// <typeparam name="T">The type of items being enumerated.</typeparam>
 202      /// <param name="tryGetNext">The callback to get next item.</param>
 203      /// <param name="dispose">The optional disposing callback.</param>
 204      /// <returns></returns>
 205      public static WrappingEnumerationStartInfo<T> CreateSynchronousWrappingStartInfo<T>(
 206         TryGetNextDelegate<T> tryGetNext,
 207         EnumerationEndedDelegate dispose = null
 208         )
 209      {
 210         const Int32 INITIAL = 0;
 211         const Int32 STARTED = 1;
 212         var state = INITIAL;
 213         return CreateWrappingStartInfo(
 214            () =>
 215            {
 216               return TaskUtils.TaskFromBoolean( Interlocked.CompareExchange( ref state, STARTED, INITIAL ) == INITIAL )
 217            },
 218            tryGetNext,
 219            dispose
 220            );
 221
 222      }
 223   }
 224
 225   internal static class SequentialCurrentInfoFactory
 226   {
 1227      private static readonly Dictionary<Type, TSequentialCurrentInfoFactory> CustomFactories = new Dictionary<Type, TSe
 1228      {
 8229         { typeof(Int32), (moveNext, disposeAsync) => new SequentialEnumeratorCurrentInfoWithInt32((MoveNextAsyncDelegat
 2230         { typeof(Int64), (moveNext, disposeAsync) => new SequentialEnumeratorCurrentInfoWithInt64((MoveNextAsyncDelegat
 2231         { typeof(Single), (moveNext, disposeAsync) => new SequentialEnumeratorCurrentInfoWithFloat32((MoveNextAsyncDele
 2232         { typeof(Double), (moveNext, disposeAsync) => new SequentialEnumeratorCurrentInfoWithFloat64((MoveNextAsyncDele
 1233      };
 234
 235      internal static TSequentialCurrentInfoFactory GetFactory( Type type )
 236      {
 11237         CustomFactories.TryGetValue( type, out var retVal );
 11238         return retVal;
 239      }
 240
 241      internal static SequentialEnumeratorCurrentInfo<T> GetInstance<T>(
 242         MoveNextAsyncDelegate<T> moveNext,
 243         EnumerationEndedDelegate dispose
 244         )
 245      {
 11246         return (SequentialEnumeratorCurrentInfo<T>) GetFactory( typeof( T ) )?.Invoke( moveNext, dispose ) ?? new Seque
 247      }
 248   }
 249
 250   /// <summary>
 251   /// This delegate will be used by the <see cref="IAsyncEnumerator{T}.WaitForNextAsync"/> method of <see cref="IAsyncE
 252   /// </summary>
 253   /// <typeparam name="T">The type of the items being enumerated.</typeparam>
 254   /// <returns>A value task with information about next item fetched.</returns>
 255   /// <remarks>
 256   /// The information is a tuple, where the elements are interpreted as following:
 257   /// <list type="number">
 258   /// <item><term><see cref="System.Boolean"/></term><description>Whether this fetch was a success. If enumerable has n
 259   /// <item><term><typeparamref name="T"/></term><description>The item fetched.</description></item>
 260   /// </list>
 261   /// </remarks>
 262   /// <seealso cref="AsyncEnumerationFactory.CreateSequentialEnumerator{T}(MoveNextAsyncDelegate{T}, EnumerationEndedDe
 263   public delegate ValueTask<(Boolean, T)> MoveNextAsyncDelegate<T>();
 264
 265   /// <summary>
 266   /// This delegate will be used by <see cref="IAsyncDisposable.DisposeAsync"/> methods when enumeration end is encount
 267   /// </summary>
 268   /// <returns>A task to perform asynchronous disposing. If no asynchronous disposing is needed, this delegate can retu
 269   /// <seealso cref="AsyncEnumerationFactory.CreateSequentialEnumerator{T}(MoveNextAsyncDelegate{T}, EnumerationEndedDe
 270   public delegate Task EnumerationEndedDelegate();
 271
 272   /// <summary>
 273   /// This struct captures information required for callback-based <see cref="IAsyncEnumerator{T}"/>.
 274   /// </summary>
 275   /// <typeparam name="T">The type of items being enumerated.</typeparam>
 276   /// <seealso cref="AsyncEnumerationFactory.CreateSequentialEnumerable"/>
 277   /// <seealso cref="AsyncEnumerationFactory.CreateSequentialStartInfo"/>
 278   public struct SequentialEnumerationStartInfo<T>
 279   {
 280      /// <summary>
 281      /// Initializes a new <see cref="SequentialEnumerationStartInfo{T}"/> with given callbacks.
 282      /// </summary>
 283      /// <param name="moveNext">The callback to fetch next item.</param>
 284      /// <param name="dispose">The optional callback to dispose enumerator. May be <c>null</c>.</param>
 285      /// <remarks>
 286      /// If <paramref name="moveNext"/> is <c>null</c>, then enumeration ends immediately.
 287      /// </remarks>
 288      /// <seealso cref="AsyncEnumerationFactory.CreateSequentialStartInfo"/>
 289      public SequentialEnumerationStartInfo(
 290         MoveNextAsyncDelegate<T> moveNext,
 291         EnumerationEndedDelegate dispose
 292         )
 293      {
 294         this.MoveNext = moveNext;
 295         this.Dispose = dispose;
 296      }
 297
 298      /// <summary>
 299      /// Gets the callback to potentially asynchronously fetch the next item.
 300      /// </summary>
 301      /// <value>The callback to potentially asynchronously fetch the next item.</value>
 302      public MoveNextAsyncDelegate<T> MoveNext { get; }
 303
 304      /// <summary>
 305      /// Gets the callback to dispose enumerator.
 306      /// </summary>
 307      /// <value>The callback to dispose enumerator.</value>
 308      public EnumerationEndedDelegate Dispose { get; }
 309   }
 310
 311   /// <summary>
 312   /// This delegate captures signature of <see cref="IAsyncEnumerator{T}.WaitForNextAsync"/>, and is used by <see cref=
 313   /// </summary>
 314   /// <returns>Potentially asynchronously returns value indicating whether there are more elements to be enumerated.</r
 315   public delegate Task<Boolean> WaitForNextDelegate();
 316
 317   /// <summary>
 318   /// This delegate captures signature of <see cref="IAsyncEnumerator{T}.TryGetNext"/>, and is used by <see cref="Wrapp
 319   /// </summary>
 320   /// <typeparam name="T">The type of items being enumerated.</typeparam>
 321   /// <param name="success">Whether this call was successful in retrieving next item.</param>
 322   /// <returns>The next item.</returns>
 323   public delegate T TryGetNextDelegate<T>( out Boolean success );
 324
 325   /// <summary>
 326   /// This type contains a set of delegates required to mimic <see cref="IAsyncEnumerator{T}"/>.
 327   /// It is used by <see cref="AsyncEnumerationFactory.CreateStatefulWrappingEnumerable"/> and <see cref="AsyncEnumerat
 328   /// The <see cref="AsyncEnumerationFactory.CreateWrappingStartInfo"/> can be used to create instances of this type wh
 329   /// </summary>
 330   /// <typeparam name="T">The type of items being enumerated.</typeparam>
 331   public struct WrappingEnumerationStartInfo<T>
 332   {
 333      /// <summary>
 334      /// Creates a new instance of <see cref="WrappingEnumerationStartInfo{T}"/>.
 335      /// </summary>
 336      /// <param name="waitForNext">The callback that will be used by <see cref="IAsyncEnumerator{T}.WaitForNextAsync"/>
 337      /// <param name="tryGetNext">The callback that will be used by <see cref="IAsyncEnumerator{T}.TryGetNext"/>.</para
 338      /// <param name="dispose">The optional callback that will be used by <see cref="IAsyncDisposable.DisposeAsync"/>.<
 339      /// <exception cref="ArgumentNullException">If either of <paramref name="waitForNext"/> or <paramref name="tryGetN
 340      public WrappingEnumerationStartInfo(
 341         WaitForNextDelegate waitForNext,
 342         TryGetNextDelegate<T> tryGetNext,
 343         EnumerationEndedDelegate dispose
 344         )
 345      {
 346         this.WaitForNext = waitForNext;
 347         this.TryGetNext = tryGetNext;
 348         this.Dispose = dispose;
 349      }
 350
 351      /// <summary>
 352      /// Gets the callback for <see cref="IAsyncEnumerator{T}.WaitForNextAsync"/> method.
 353      /// </summary>
 354      /// <value>The callback for <see cref="IAsyncEnumerator{T}.WaitForNextAsync"/> method.</value>
 355      /// <seealso cref="WaitForNextDelegate"/>
 356      public WaitForNextDelegate WaitForNext { get; }
 357
 358      /// <summary>
 359      /// Gets the callback for <see cref="IAsyncEnumerator{T}.TryGetNext"/> method.
 360      /// </summary>
 361      /// <value>The callback for <see cref="IAsyncEnumerator{T}.TryGetNext"/> method.</value>
 362      /// <seealso cref="WaitForNextDelegate"/>
 363      public TryGetNextDelegate<T> TryGetNext { get; }
 364
 365      /// <summary>
 366      /// Gets the callback for <see cref="IAsyncDisposable.DisposeAsync"/> method.
 367      /// </summary>
 368      /// <value>The callback for <see cref="IAsyncDisposable.DisposeAsync"/> method.</value>
 369      /// <seealso cref="EnumerationEndedDelegate"/>
 370      public EnumerationEndedDelegate Dispose { get; }
 371   }
 372}