LCOV - code coverage report
Current view: top level - stream_channel-1.6.2/lib/src - guarantee_channel.dart (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 0 59 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             : /// A [StreamChannel] that enforces the stream channel guarantees.
      12             : ///
      13             : /// This is exposed via [new StreamChannel.withGuarantees].
      14             : class GuaranteeChannel<T> extends StreamChannelMixin<T> {
      15           0 :   Stream<T> get stream => _streamController.stream;
      16             : 
      17           0 :   StreamSink<T> get sink => _sink;
      18             :   _GuaranteeSink<T> _sink;
      19             : 
      20             :   /// The controller for [stream].
      21             :   ///
      22             :   /// This intermediate controller allows us to continue listening for a done
      23             :   /// event even after the user has canceled their subscription, and to send our
      24             :   /// own done event when the sink is closed.
      25             :   StreamController<T> _streamController;
      26             : 
      27             :   /// The subscription to the inner stream.
      28             :   StreamSubscription<T> _subscription;
      29             : 
      30             :   /// Whether the sink has closed, causing the underlying channel to disconnect.
      31             :   bool _disconnected = false;
      32             : 
      33             :   GuaranteeChannel(Stream<T> innerStream, StreamSink<T> innerSink,
      34           0 :       {bool allowSinkErrors: true}) {
      35           0 :     _sink =
      36           0 :         new _GuaranteeSink<T>(innerSink, this, allowErrors: allowSinkErrors);
      37             : 
      38             :     // Enforce the single-subscription guarantee by changing a broadcast stream
      39             :     // to single-subscription.
      40           0 :     if (innerStream.isBroadcast) {
      41             :       innerStream =
      42           0 :           innerStream.transform(const SingleSubscriptionTransformer());
      43             :     }
      44             : 
      45           0 :     _streamController = new StreamController<T>(
      46             :         onListen: () {
      47             :           // If the sink has disconnected, we've already called
      48             :           // [_streamController.close].
      49           0 :           if (_disconnected) return;
      50             : 
      51           0 :           _subscription = innerStream.listen(_streamController.add,
      52           0 :               onError: _streamController.addError, onDone: () {
      53           0 :             _sink._onStreamDisconnected();
      54           0 :             _streamController.close();
      55             :           });
      56             :         },
      57             :         sync: true);
      58             :   }
      59             : 
      60             :   /// Called by [_GuaranteeSink] when the user closes it.
      61             :   ///
      62             :   /// The sink closing indicates that the connection is closed, so the stream
      63             :   /// should stop emitting events.
      64             :   void _onSinkDisconnected() {
      65           0 :     _disconnected = true;
      66           0 :     if (_subscription != null) _subscription.cancel();
      67           0 :     _streamController.close();
      68             :   }
      69             : }
      70             : 
      71             : /// The sink for [GuaranteeChannel].
      72             : ///
      73             : /// This wraps the inner sink to ignore events and cancel any in-progress
      74             : /// [addStream] calls when the underlying channel closes.
      75             : class _GuaranteeSink<T> implements StreamSink<T> {
      76             :   /// The inner sink being wrapped.
      77             :   final StreamSink<T> _inner;
      78             : 
      79             :   /// The [GuaranteeChannel] this belongs to.
      80             :   final GuaranteeChannel<T> _channel;
      81             : 
      82           0 :   Future get done => _doneCompleter.future;
      83             :   final _doneCompleter = new Completer();
      84             : 
      85             :   /// Whether connection is disconnected.
      86             :   ///
      87             :   /// This can happen because the stream has emitted a done event, or because
      88             :   /// the user added an error when [_allowErrors] is `false`.
      89             :   bool _disconnected = false;
      90             : 
      91             :   /// Whether the user has called [close].
      92             :   bool _closed = false;
      93             : 
      94             :   /// The subscription to the stream passed to [addStream], if a stream is
      95             :   /// currently being added.
      96             :   StreamSubscription<T> _addStreamSubscription;
      97             : 
      98             :   /// The completer for the future returned by [addStream], if a stream is
      99             :   /// currently being added.
     100             :   Completer _addStreamCompleter;
     101             : 
     102             :   /// Whether we're currently adding a stream with [addStream].
     103           0 :   bool get _inAddStream => _addStreamSubscription != null;
     104             : 
     105             :   /// Whether errors are passed on to the underlying sink.
     106             :   ///
     107             :   /// If this is `false`, any error passed to the sink is piped to [done] and
     108             :   /// the underlying sink is closed.
     109             :   final bool _allowErrors;
     110             : 
     111             :   _GuaranteeSink(this._inner, this._channel, {bool allowErrors: true})
     112           0 :       : _allowErrors = allowErrors;
     113             : 
     114             :   void add(T data) {
     115           0 :     if (_closed) throw new StateError("Cannot add event after closing.");
     116           0 :     if (_inAddStream) {
     117           0 :       throw new StateError("Cannot add event while adding stream.");
     118             :     }
     119           0 :     if (_disconnected) return;
     120             : 
     121           0 :     _inner.add(data);
     122             :   }
     123             : 
     124             :   void addError(error, [StackTrace stackTrace]) {
     125           0 :     if (_closed) throw new StateError("Cannot add event after closing.");
     126           0 :     if (_inAddStream) {
     127           0 :       throw new StateError("Cannot add event while adding stream.");
     128             :     }
     129           0 :     if (_disconnected) return;
     130             : 
     131           0 :     _addError(error, stackTrace);
     132             :   }
     133             : 
     134             :   /// Like [addError], but doesn't check to ensure that an error can be added.
     135             :   ///
     136             :   /// This is called from [addStream], so it shouldn't fail if a stream is being
     137             :   /// added.
     138             :   void _addError(error, [StackTrace stackTrace]) {
     139           0 :     if (_allowErrors) {
     140           0 :       _inner.addError(error, stackTrace);
     141             :       return;
     142             :     }
     143             : 
     144           0 :     _doneCompleter.completeError(error, stackTrace);
     145             : 
     146             :     // Treat an error like both the stream and sink disconnecting.
     147           0 :     _onStreamDisconnected();
     148           0 :     _channel._onSinkDisconnected();
     149             : 
     150             :     // Ignore errors from the inner sink. We're already surfacing one error, and
     151             :     // if the user handles it we don't want them to have another top-level.
     152           0 :     _inner.close().catchError((_) {});
     153             :   }
     154             : 
     155             :   Future addStream(Stream<T> stream) {
     156           0 :     if (_closed) throw new StateError("Cannot add stream after closing.");
     157           0 :     if (_inAddStream) {
     158           0 :       throw new StateError("Cannot add stream while adding stream.");
     159             :     }
     160           0 :     if (_disconnected) return new Future.value();
     161             : 
     162           0 :     _addStreamCompleter = new Completer.sync();
     163           0 :     _addStreamSubscription = stream.listen(_inner.add,
     164           0 :         onError: _addError, onDone: _addStreamCompleter.complete);
     165           0 :     return _addStreamCompleter.future.then((_) {
     166           0 :       _addStreamCompleter = null;
     167           0 :       _addStreamSubscription = null;
     168             :     });
     169             :   }
     170             : 
     171             :   Future close() {
     172           0 :     if (_inAddStream) {
     173           0 :       throw new StateError("Cannot close sink while adding stream.");
     174             :     }
     175             : 
     176           0 :     if (_closed) return done;
     177           0 :     _closed = true;
     178             : 
     179           0 :     if (!_disconnected) {
     180           0 :       _channel._onSinkDisconnected();
     181           0 :       _doneCompleter.complete(_inner.close());
     182             :     }
     183             : 
     184           0 :     return done;
     185             :   }
     186             : 
     187             :   /// Called by [GuaranteeChannel] when the stream emits a done event.
     188             :   ///
     189             :   /// The stream being done indicates that the connection is closed, so the
     190             :   /// sink should stop forwarding events.
     191             :   void _onStreamDisconnected() {
     192           0 :     _disconnected = true;
     193           0 :     if (!_doneCompleter.isCompleted) _doneCompleter.complete();
     194             : 
     195           0 :     if (!_inAddStream) return;
     196           0 :     _addStreamCompleter.complete(_addStreamSubscription.cancel());
     197           0 :     _addStreamCompleter = null;
     198           0 :     _addStreamSubscription = null;
     199             :   }
     200             : }

Generated by: LCOV version 1.13