LCOV - code coverage report
Current view: top level - stream_channel-1.6.2/lib/src - disconnector.dart (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 0 43 0.0 %
Date: 2017-10-10 20:17:03 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 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 = new AsyncMemoizer();
      42             : 
      43             :   StreamChannel<T> bind(StreamChannel<T> channel) {
      44           0 :     return channel.changeSink((innerSink) {
      45           0 :       var sink = new _DisconnectorSink<T>(innerSink);
      46             : 
      47           0 :       if (isDisconnected) {
      48             :         // Ignore errors here, because otherwise there would be no way for the
      49             :         // user to handle them gracefully.
      50           0 :         sink._disconnect().catchError((_) {});
      51             :       } else {
      52           0 :         _sinks.add(sink);
      53             :       }
      54             : 
      55             :       return sink;
      56             :     });
      57             :   }
      58             : }
      59             : 
      60             : /// A sink wrapper that can force a disconnection.
      61             : class _DisconnectorSink<T> implements StreamSink<T> {
      62             :   /// The inner sink.
      63             :   final StreamSink<T> _inner;
      64             : 
      65           0 :   Future get done => _inner.done;
      66             : 
      67             :   /// Whether [Disconnector.disconnect] has been called.
      68             :   var _isDisconnected = false;
      69             : 
      70             :   /// Whether the user has called [close].
      71             :   var _closed = false;
      72             : 
      73             :   /// The subscription to the stream passed to [addStream], if a stream is
      74             :   /// currently being added.
      75             :   StreamSubscription<T> _addStreamSubscription;
      76             : 
      77             :   /// The completer for the future returned by [addStream], if a stream is
      78             :   /// currently being added.
      79             :   Completer _addStreamCompleter;
      80             : 
      81             :   /// Whether we're currently adding a stream with [addStream].
      82           0 :   bool get _inAddStream => _addStreamSubscription != null;
      83             : 
      84           0 :   _DisconnectorSink(this._inner);
      85             : 
      86             :   void add(T data) {
      87           0 :     if (_closed) throw new StateError("Cannot add event after closing.");
      88           0 :     if (_inAddStream) {
      89           0 :       throw new StateError("Cannot add event while adding stream.");
      90             :     }
      91           0 :     if (_isDisconnected) return;
      92             : 
      93           0 :     _inner.add(data);
      94             :   }
      95             : 
      96             :   void addError(error, [StackTrace stackTrace]) {
      97           0 :     if (_closed) throw new StateError("Cannot add event after closing.");
      98           0 :     if (_inAddStream) {
      99           0 :       throw new StateError("Cannot add event while adding stream.");
     100             :     }
     101           0 :     if (_isDisconnected) return;
     102             : 
     103           0 :     _inner.addError(error, stackTrace);
     104             :   }
     105             : 
     106             :   Future addStream(Stream<T> stream) {
     107           0 :     if (_closed) throw new StateError("Cannot add stream after closing.");
     108           0 :     if (_inAddStream) {
     109           0 :       throw new StateError("Cannot add stream while adding stream.");
     110             :     }
     111           0 :     if (_isDisconnected) return new Future.value();
     112             : 
     113           0 :     _addStreamCompleter = new Completer.sync();
     114           0 :     _addStreamSubscription = stream.listen(_inner.add,
     115           0 :         onError: _inner.addError, onDone: _addStreamCompleter.complete);
     116           0 :     return _addStreamCompleter.future.then((_) {
     117           0 :       _addStreamCompleter = null;
     118           0 :       _addStreamSubscription = null;
     119             :     });
     120             :   }
     121             : 
     122             :   Future close() {
     123           0 :     if (_inAddStream) {
     124           0 :       throw new StateError("Cannot close sink while adding stream.");
     125             :     }
     126             : 
     127           0 :     _closed = true;
     128           0 :     return _inner.close();
     129             :   }
     130             : 
     131             :   /// Disconnects this sink.
     132             :   ///
     133             :   /// This closes the underlying sink and stops forwarding events. It returns
     134             :   /// the [StreamSink.close] future for the underlying sink.
     135             :   Future _disconnect() {
     136           0 :     _isDisconnected = true;
     137           0 :     var future = _inner.close();
     138             : 
     139           0 :     if (_inAddStream) {
     140           0 :       _addStreamCompleter.complete(_addStreamSubscription.cancel());
     141           0 :       _addStreamCompleter = null;
     142           0 :       _addStreamSubscription = null;
     143             :     }
     144             : 
     145             :     return future;
     146             :   }
     147             : }

Generated by: LCOV version 1.13