LCOV - code coverage report
Current view: top level - stream_channel-2.1.0/lib/src - disconnector.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 50 0.0 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
       2             : // for details. All rights reserved. Use of this source code is governed by a
       3             : // BSD-style license that can be found in the LICENSE file.
       4             : 
       5             : import 'dart:async';
       6             : 
       7             : import 'package:async/async.dart';
       8             : 
       9             : import '../stream_channel.dart';
      10             : 
      11             : /// Allows the caller to force a channel to disconnect.
      12             : ///
      13             : /// When [disconnect] is called, the channel (or channels) transformed by this
      14             : /// transformer will act as though the remote end had disconnected—the stream
      15             : /// will emit a done event, and the sink will ignore future inputs. The inner
      16             : /// sink will also be closed to notify the remote end of the disconnection.
      17             : ///
      18             : /// If a channel is transformed after the [disconnect] has been called, it will
      19             : /// be disconnected immediately.
      20             : class Disconnector<T> implements StreamChannelTransformer<T, T> {
      21             :   /// Whether [disconnect] has been called.
      22           0 :   bool get isDisconnected => _disconnectMemo.hasRun;
      23             : 
      24             :   /// The sinks for transformed channels.
      25             :   ///
      26             :   /// Note that we assume that transformed channels provide the stream channel
      27             :   /// guarantees. This allows us to only track sinks, because we know closing
      28             :   /// the underlying sink will cause the stream to emit a done event.
      29             :   final _sinks = <_DisconnectorSink<T>>[];
      30             : 
      31             :   /// Disconnects all channels that have been transformed.
      32             :   ///
      33             :   /// Returns a future that completes when all inner sinks' [StreamSink.close]
      34             :   /// futures have completed. Note that a [StreamController]'s sink won't close
      35             :   /// until the corresponding stream has a listener.
      36           0 :   Future<void> disconnect() => _disconnectMemo.runOnce(() {
      37           0 :         var futures = _sinks.map((sink) => sink._disconnect()).toList();
      38           0 :         _sinks.clear();
      39           0 :         return Future.wait(futures, eagerError: true);
      40             :       });
      41             :   final _disconnectMemo = AsyncMemoizer();
      42             : 
      43           0 :   @override
      44             :   StreamChannel<T> bind(StreamChannel<T> channel) {
      45           0 :     return channel.changeSink((innerSink) {
      46           0 :       var sink = _DisconnectorSink<T>(innerSink);
      47             : 
      48           0 :       if (isDisconnected) {
      49             :         // Ignore errors here, because otherwise there would be no way for the
      50             :         // user to handle them gracefully.
      51           0 :         sink._disconnect().catchError((_) {});
      52             :       } else {
      53           0 :         _sinks.add(sink);
      54             :       }
      55             : 
      56             :       return sink;
      57             :     });
      58             :   }
      59             : }
      60             : 
      61             : /// A sink wrapper that can force a disconnection.
      62             : class _DisconnectorSink<T> implements StreamSink<T> {
      63             :   /// The inner sink.
      64             :   final StreamSink<T> _inner;
      65             : 
      66           0 :   @override
      67           0 :   Future<void> get done => _inner.done;
      68             : 
      69             :   /// Whether [Disconnector.disconnect] has been called.
      70             :   var _isDisconnected = false;
      71             : 
      72             :   /// Whether the user has called [close].
      73             :   var _closed = false;
      74             : 
      75             :   /// The subscription to the stream passed to [addStream], if a stream is
      76             :   /// currently being added.
      77             :   StreamSubscription<T>? _addStreamSubscription;
      78             : 
      79             :   /// The completer for the future returned by [addStream], if a stream is
      80             :   /// currently being added.
      81             :   Completer? _addStreamCompleter;
      82             : 
      83             :   /// Whether we're currently adding a stream with [addStream].
      84           0 :   bool get _inAddStream => _addStreamSubscription != null;
      85             : 
      86           0 :   _DisconnectorSink(this._inner);
      87             : 
      88           0 :   @override
      89             :   void add(T data) {
      90           0 :     if (_closed) throw StateError('Cannot add event after closing.');
      91           0 :     if (_inAddStream) {
      92           0 :       throw StateError('Cannot add event while adding stream.');
      93             :     }
      94           0 :     if (_isDisconnected) return;
      95             : 
      96           0 :     _inner.add(data);
      97             :   }
      98             : 
      99           0 :   @override
     100             :   void addError(error, [StackTrace? stackTrace]) {
     101           0 :     if (_closed) throw StateError('Cannot add event after closing.');
     102           0 :     if (_inAddStream) {
     103           0 :       throw StateError('Cannot add event while adding stream.');
     104             :     }
     105           0 :     if (_isDisconnected) return;
     106             : 
     107           0 :     _inner.addError(error, stackTrace);
     108             :   }
     109             : 
     110           0 :   @override
     111             :   Future<void> addStream(Stream<T> stream) {
     112           0 :     if (_closed) throw StateError('Cannot add stream after closing.');
     113           0 :     if (_inAddStream) {
     114           0 :       throw StateError('Cannot add stream while adding stream.');
     115             :     }
     116           0 :     if (_isDisconnected) return Future.value();
     117             : 
     118           0 :     _addStreamCompleter = Completer.sync();
     119           0 :     _addStreamSubscription = stream.listen(_inner.add,
     120           0 :         onError: _inner.addError, onDone: _addStreamCompleter!.complete);
     121           0 :     return _addStreamCompleter!.future.then((_) {
     122           0 :       _addStreamCompleter = null;
     123           0 :       _addStreamSubscription = null;
     124             :     });
     125             :   }
     126             : 
     127           0 :   @override
     128             :   Future<void> close() {
     129           0 :     if (_inAddStream) {
     130           0 :       throw StateError('Cannot close sink while adding stream.');
     131             :     }
     132             : 
     133           0 :     _closed = true;
     134           0 :     return _inner.close();
     135             :   }
     136             : 
     137             :   /// Disconnects this sink.
     138             :   ///
     139             :   /// This closes the underlying sink and stops forwarding events. It returns
     140             :   /// the [StreamSink.close] future for the underlying sink.
     141           0 :   Future<void> _disconnect() {
     142           0 :     _isDisconnected = true;
     143           0 :     var future = _inner.close();
     144             : 
     145           0 :     if (_inAddStream) {
     146           0 :       _addStreamCompleter!.complete(_addStreamSubscription!.cancel());
     147           0 :       _addStreamCompleter = null;
     148           0 :       _addStreamSubscription = null;
     149             :     }
     150             : 
     151             :     return future;
     152             :   }
     153             : }

Generated by: LCOV version 1.14