csharp/akkadotnet/Akka.Streams.Kafka/src/Akka.Streams.Kafka/Helpers/Control.cs

Control.cs
using System;
using System.Threading.Tasks;
using Akka.Streams.Kafka.Extensions;
using Confluent.Kafka;

namespace Akka.Streams.Kafka.Helpers
{
    /// 
    /// Materialized value of the consumer `Source`.
    /// 
    public interface IControl
    {
        /// 
        /// Stop producing messages from the `Source`. This does not stop the underlying kafka consumer
        /// and does not unsubscribe from any topics/parsations.
        ///
        /// Call  to close consumer.
        /// 
        Task Stop();

        /// 
        /// Shutdown the consumer `Source`. It will wait for outstanding offset
        /// commit requests to finish before shutting down.
        /// 
        Task Shutdown();

        /// 
        /// Shutdown status. The task will be completed when the stage has been shut down
        /// and the underlying  has been closed. Shutdown can be triggered
        /// from downstream cancellation, errors, or 
        /// 
        Task IsShutdown { get; }

        /// 
        /// Stop producing messages from the `Source`, wait for stream completion
        /// and shut down the consumer `Source` so that all consumed messages
        /// reach the end of the stream.
        /// Failures in stream completion will be propagated, the source will be shut down anyway.
        /// 
        Task DrainAndShutdown(Task streamCompletion);
    }

    /// 
    /// Combine control and a stream completion signal materialized values into
    /// one, so that the stream can be stopped in a controlled way without losing
    /// commits.
    /// 
    /// Stream completion result type
    public clast DrainingControl : IControl
    {
        public IControl Control { get; }
        public Task StreamCompletion { get; }

        /// 
        /// DrainingControl
        /// 
        /// 
        /// 
        private DrainingControl(IControl control, Task streamCompletion)
        {
            Control = control;
            StreamCompletion = streamCompletion;
        }

        /// 
        public Task Stop() => Control.Stop();

        /// 
        public Task Shutdown() => Control.Shutdown();

        /// 
        public Task IsShutdown => Control.IsShutdown;

        /// 
        public Task DrainAndShutdown(Task streamCompletion) => Control.DrainAndShutdown(streamCompletion);

        /// 
        /// Stop producing messages from the `Source`, wait for stream completion
        /// and shut down the consumer `Source` so that all consumed messages
        /// reach the end of the stream.
        /// 
        public Task DrainAndShutdown() => Control.DrainAndShutdown(StreamCompletion);
        
        /// 
        /// Combine control and a stream completion signal materialized values into
        /// one, so that the stream can be stopped in a controlled way without losing
        /// commits.
        /// 
        public static DrainingControl Create(IControl control, Task streamCompletion) => new DrainingControl(control, streamCompletion);
        
        /// 
        /// Combine control and a stream completion signal materialized values into
        /// one, so that the stream can be stopped in a controlled way without losing
        /// commits.
        /// 
        public static DrainingControl Create((IControl, Task) tuple) => new DrainingControl(tuple.Item1, tuple.Item2);
        
        /// 
        /// Combine control and a stream completion signal materialized values into
        /// one, so that the stream can be stopped in a controlled way without losing
        /// commits.
        /// 
        public static DrainingControl Create((IControl, Task) tuple)
        {
            return new DrainingControl(tuple.Item1, tuple.Item2.ContinueWith(t => NotUsed.Instance, TaskContinuationOptions.NotOnFaulted));
        }

        public static DrainingControl Create(IControl control, Task streamCompletion)
        {
            return new DrainingControl(control, streamCompletion.ContinueWith(t => NotUsed.Instance, TaskContinuationOptions.NotOnFaulted));
        }

    }

    /// 
    /// An implementation of Control to be used as an empty value, all methods return a failed task.
    /// 
    public clast NoopControl : IControl
    {
        private static Exception Exception => new Exception("The correct Consumer.Control has not been astigned, yet.");

        /// 
        public Task Stop() => Task.FromException(Exception);

        /// 
        public Task Shutdown() => Task.FromException(Exception);

        /// 
        public Task IsShutdown => Task.FromException(Exception);

        /// 
        public Task DrainAndShutdown(Task streamCompletion) => this.DrainAndShutdownDefault(streamCompletion);
    }
}