LCOV - code coverage report
Current view: top level - async-2.5.0/lib/src - stream_queue.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 56 238 23.5 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright (c) 2015, 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             : import 'dart:collection';
       7             : 
       8             : import 'package:collection/collection.dart';
       9             : 
      10             : import 'cancelable_operation.dart';
      11             : import 'result/result.dart';
      12             : import 'subscription_stream.dart';
      13             : import 'stream_completer.dart';
      14             : import 'stream_splitter.dart';
      15             : 
      16             : /// An asynchronous pull-based interface for accessing stream events.
      17             : ///
      18             : /// Wraps a stream and makes individual events available on request.
      19             : ///
      20             : /// You can request (and reserve) one or more events from the stream,
      21             : /// and after all previous requests have been fulfilled, stream events
      22             : /// go towards fulfilling your request.
      23             : ///
      24             : /// For example, if you ask for [next] two times, the returned futures
      25             : /// will be completed by the next two unrequested events from the stream.
      26             : ///
      27             : /// The stream subscription is paused when there are no active
      28             : /// requests.
      29             : ///
      30             : /// Some streams, including broadcast streams, will buffer
      31             : /// events while paused, so waiting too long between requests may
      32             : /// cause memory bloat somewhere else.
      33             : ///
      34             : /// This is similar to, but more convenient than, a [StreamIterator].
      35             : /// A `StreamIterator` requires you to manually check when a new event is
      36             : /// available and you can only access the value of that event until you
      37             : /// check for the next one. A `StreamQueue` allows you to request, for example,
      38             : /// three events at a time, either individually, as a group using [take]
      39             : /// or [skip], or in any combination.
      40             : ///
      41             : /// You can also ask to have the [rest] of the stream provided as
      42             : /// a new stream. This allows, for example, taking the first event
      43             : /// out of a stream and continuing to use the rest of the stream as a stream.
      44             : ///
      45             : /// Example:
      46             : ///
      47             : ///     var events = StreamQueue<String>(someStreamOfLines);
      48             : ///     var first = await events.next;
      49             : ///     while (first.startsWith('#')) {
      50             : ///       // Skip comments.
      51             : ///       first = await events.next;
      52             : ///     }
      53             : ///
      54             : ///     if (first.startsWith(MAGIC_MARKER)) {
      55             : ///       var headerCount =
      56             : ///           first.parseInt(first.substring(MAGIC_MARKER.length + 1));
      57             : ///       handleMessage(headers: await events.take(headerCount),
      58             : ///                     body: events.rest);
      59             : ///       return;
      60             : ///     }
      61             : ///     // Error handling.
      62             : ///
      63             : /// When you need no further events the `StreamQueue` should be closed
      64             : /// using [cancel]. This releases the underlying stream subscription.
      65             : class StreamQueue<T> {
      66             :   // This class maintains two queues: one of events and one of requests.
      67             :   // The active request (the one in front of the queue) is called with
      68             :   // the current event queue when it becomes active, every time a
      69             :   // new event arrives, and when the event source closes.
      70             :   //
      71             :   // If the request returns `true`, it's complete and will be removed from the
      72             :   // request queue.
      73             :   // If the request returns `false`, it needs more events, and will be called
      74             :   // again when new events are available. It may trigger a call itself by
      75             :   // calling [_updateRequests].
      76             :   // The request can remove events that it uses, or keep them in the event
      77             :   // queue until it has all that it needs.
      78             :   //
      79             :   // This model is very flexible and easily extensible.
      80             :   // It allows requests that don't consume events (like [hasNext]) or
      81             :   // potentially a request that takes either five or zero events, determined
      82             :   // by the content of the fifth event.
      83             : 
      84             :   final Stream<T> _source;
      85             : 
      86             :   /// Subscription on [_source] while listening for events.
      87             :   ///
      88             :   /// Set to subscription when listening, and set to `null` when the
      89             :   /// subscription is done (and [_isDone] is set to true).
      90             :   StreamSubscription<T>? _subscription;
      91             : 
      92             :   /// Whether the event source is done.
      93             :   bool _isDone = false;
      94             : 
      95             :   /// Whether a closing operation has been performed on the stream queue.
      96             :   ///
      97             :   /// Closing operations are [cancel] and [rest].
      98             :   bool _isClosed = false;
      99             : 
     100             :   /// The number of events dispatched by this queue.
     101             :   ///
     102             :   /// This counts error events. It doesn't count done events, or events
     103             :   /// dispatched to a stream returned by [rest].
     104           0 :   int get eventsDispatched => _eventsReceived - _eventQueue.length;
     105             : 
     106             :   /// The number of events received by this queue.
     107             :   var _eventsReceived = 0;
     108             : 
     109             :   /// Queue of events not used by a request yet.
     110             :   final QueueList<Result<T>> _eventQueue = QueueList();
     111             : 
     112             :   /// Queue of pending requests.
     113             :   ///
     114             :   /// Access through methods below to ensure consistency.
     115             :   final Queue<_EventRequest> _requestQueue = Queue();
     116             : 
     117             :   /// Create a `StreamQueue` of the events of [source].
     118          22 :   factory StreamQueue(Stream<T> source) => StreamQueue._(source);
     119             : 
     120             :   // Private generative constructor to avoid subclasses.
     121          11 :   StreamQueue._(this._source) {
     122             :     // Start listening immediately if we could otherwise lose events.
     123          22 :     if (_source.isBroadcast) {
     124           0 :       _ensureListening();
     125           0 :       _pause();
     126             :     }
     127             :   }
     128             : 
     129             :   /// Asks if the stream has any more events.
     130             :   ///
     131             :   /// Returns a future that completes with `true` if the stream has any
     132             :   /// more events, whether data or error.
     133             :   /// If the stream closes without producing any more events, the returned
     134             :   /// future completes with `false`.
     135             :   ///
     136             :   /// Can be used before using [next] to avoid getting an error in the
     137             :   /// future returned by `next` in the case where there are no more events.
     138             :   /// Another alternative is to use `take(1)` which returns either zero or
     139             :   /// one events.
     140           0 :   Future<bool> get hasNext {
     141           0 :     if (!_isClosed) {
     142           0 :       var hasNextRequest = _HasNextRequest<T>();
     143           0 :       _addRequest(hasNextRequest);
     144           0 :       return hasNextRequest.future;
     145             :     }
     146           0 :     throw _failClosed();
     147             :   }
     148             : 
     149             :   /// Look at the next [count] data events without consuming them.
     150             :   ///
     151             :   /// Works like [take] except that the events are left in the queue.
     152             :   /// If one of the next [count] events is an error, the returned future
     153             :   /// completes with this error, and the error is still left in the queue.
     154           0 :   Future<List<T>> lookAhead(int count) {
     155           0 :     if (count < 0) throw RangeError.range(count, 0, null, 'count');
     156           0 :     if (!_isClosed) {
     157           0 :       var request = _LookAheadRequest<T>(count);
     158           0 :       _addRequest(request);
     159           0 :       return request.future;
     160             :     }
     161           0 :     throw _failClosed();
     162             :   }
     163             : 
     164             :   /// Requests the next (yet unrequested) event from the stream.
     165             :   ///
     166             :   /// When the requested event arrives, the returned future is completed with
     167             :   /// the event.
     168             :   /// If the event is a data event, the returned future completes
     169             :   /// with its value.
     170             :   /// If the event is an error event, the returned future completes with
     171             :   /// its error and stack trace.
     172             :   /// If the stream closes before an event arrives, the returned future
     173             :   /// completes with a [StateError].
     174             :   ///
     175             :   /// It's possible to have several pending [next] calls (or other requests),
     176             :   /// and they will be completed in the order they were requested, by the
     177             :   /// first events that were not consumed by previous requeusts.
     178          11 :   Future<T> get next {
     179          11 :     if (!_isClosed) {
     180          11 :       var nextRequest = _NextRequest<T>();
     181          11 :       _addRequest(nextRequest);
     182          11 :       return nextRequest.future;
     183             :     }
     184           0 :     throw _failClosed();
     185             :   }
     186             : 
     187             :   /// Looks at the next (yet unrequested) event from the stream.
     188             :   ///
     189             :   /// Like [next] except that the event is not consumed.
     190             :   /// If the next event is an error event, it stays in the queue.
     191           0 :   Future<T> get peek {
     192           0 :     if (!_isClosed) {
     193           0 :       var nextRequest = _PeekRequest<T>();
     194           0 :       _addRequest(nextRequest);
     195           0 :       return nextRequest.future;
     196             :     }
     197           0 :     throw _failClosed();
     198             :   }
     199             : 
     200             :   /// Returns a stream of all the remaning events of the source stream.
     201             :   ///
     202             :   /// All requested [next], [skip] or [take] operations are completed
     203             :   /// first, and then any remaining events are provided as events of
     204             :   /// the returned stream.
     205             :   ///
     206             :   /// Using `rest` closes this stream queue. After getting the
     207             :   /// `rest` the caller may no longer request other events, like
     208             :   /// after calling [cancel].
     209          11 :   Stream<T> get rest {
     210          11 :     if (_isClosed) {
     211           0 :       throw _failClosed();
     212             :     }
     213          11 :     var request = _RestRequest<T>(this);
     214          11 :     _isClosed = true;
     215          11 :     _addRequest(request);
     216          11 :     return request.stream;
     217             :   }
     218             : 
     219             :   /// Skips the next [count] *data* events.
     220             :   ///
     221             :   /// The [count] must be non-negative.
     222             :   ///
     223             :   /// When successful, this is equivalent to using [take]
     224             :   /// and ignoring the result.
     225             :   ///
     226             :   /// If an error occurs before `count` data events have been skipped,
     227             :   /// the returned future completes with that error instead.
     228             :   ///
     229             :   /// If the stream closes before `count` data events,
     230             :   /// the remaining unskipped event count is returned.
     231             :   /// If the returned future completes with the integer `0`,
     232             :   /// then all events were succssfully skipped. If the value
     233             :   /// is greater than zero then the stream ended early.
     234           0 :   Future<int> skip(int count) {
     235           0 :     if (count < 0) throw RangeError.range(count, 0, null, 'count');
     236           0 :     if (!_isClosed) {
     237           0 :       var request = _SkipRequest<T>(count);
     238           0 :       _addRequest(request);
     239           0 :       return request.future;
     240             :     }
     241           0 :     throw _failClosed();
     242             :   }
     243             : 
     244             :   /// Requests the next [count] data events as a list.
     245             :   ///
     246             :   /// The [count] must be non-negative.
     247             :   ///
     248             :   /// Equivalent to calling [next] `count` times and
     249             :   /// storing the data values in a list.
     250             :   ///
     251             :   /// If an error occurs before `count` data events has
     252             :   /// been collected, the returned future completes with
     253             :   /// that error instead.
     254             :   ///
     255             :   /// If the stream closes before `count` data events,
     256             :   /// the returned future completes with the list
     257             :   /// of data collected so far. That is, the returned
     258             :   /// list may have fewer than [count] elements.
     259           0 :   Future<List<T>> take(int count) {
     260           0 :     if (count < 0) throw RangeError.range(count, 0, null, 'count');
     261           0 :     if (!_isClosed) {
     262           0 :       var request = _TakeRequest<T>(count);
     263           0 :       _addRequest(request);
     264           0 :       return request.future;
     265             :     }
     266           0 :     throw _failClosed();
     267             :   }
     268             : 
     269             :   /// Requests a transaction that can conditionally consume events.
     270             :   ///
     271             :   /// The transaction can create copies of this queue at the current position
     272             :   /// using [StreamQueueTransaction.newQueue]. Each of these queues is
     273             :   /// independent of one another and of the parent queue. The transaction
     274             :   /// finishes when one of two methods is called:
     275             :   ///
     276             :   /// * [StreamQueueTransaction.commit] updates the parent queue's position to
     277             :   ///   match that of one of the copies.
     278             :   ///
     279             :   /// * [StreamQueueTransaction.reject] causes the parent queue to continue as
     280             :   ///   though [startTransaction] hadn't been called.
     281             :   ///
     282             :   /// Until the transaction finishes, this queue won't emit any events.
     283             :   ///
     284             :   /// See also [withTransaction] and [cancelable].
     285             :   ///
     286             :   /// ```dart
     287             :   /// /// Consumes all empty lines from the beginning of [lines].
     288             :   /// Future consumeEmptyLines(StreamQueue<String> lines) async {
     289             :   ///   while (await lines.hasNext) {
     290             :   ///     var transaction = lines.startTransaction();
     291             :   ///     var queue = transaction.newQueue();
     292             :   ///     if ((await queue.next).isNotEmpty) {
     293             :   ///       transaction.reject();
     294             :   ///       return;
     295             :   ///     } else {
     296             :   ///       transaction.commit(queue);
     297             :   ///     }
     298             :   ///   }
     299             :   /// }
     300             :   /// ```
     301           0 :   StreamQueueTransaction<T> startTransaction() {
     302           0 :     if (_isClosed) throw _failClosed();
     303             : 
     304           0 :     var request = _TransactionRequest(this);
     305           0 :     _addRequest(request);
     306           0 :     return request.transaction;
     307             :   }
     308             : 
     309             :   /// Passes a copy of this queue to [callback], and updates this queue to match
     310             :   /// the copy's position if [callback] returns `true`.
     311             :   ///
     312             :   /// This queue won't emit any events until [callback] returns. If it returns
     313             :   /// `false`, this queue continues as though [withTransaction] hadn't been
     314             :   /// called. If it throws an error, this updates this queue to match the copy's
     315             :   /// position and throws the error from the returned `Future`.
     316             :   ///
     317             :   /// Returns the same value as [callback].
     318             :   ///
     319             :   /// See also [startTransaction] and [cancelable].
     320             :   ///
     321             :   /// ```dart
     322             :   /// /// Consumes all empty lines from the beginning of [lines].
     323             :   /// Future consumeEmptyLines(StreamQueue<String> lines) async {
     324             :   ///   while (await lines.hasNext) {
     325             :   ///     // Consume a line if it's empty, otherwise return.
     326             :   ///     if (!await lines.withTransaction(
     327             :   ///         (queue) async => (await queue.next).isEmpty)) {
     328             :   ///       return;
     329             :   ///     }
     330             :   ///   }
     331             :   /// }
     332             :   /// ```
     333           0 :   Future<bool> withTransaction(Future<bool> Function(StreamQueue<T>) callback) {
     334           0 :     var transaction = startTransaction();
     335             : 
     336             :     /// Avoid async/await to ensure that [startTransaction] is called
     337             :     /// synchronously and so ends up in the right place in the request queue.
     338           0 :     var queue = transaction.newQueue();
     339           0 :     return callback(queue).then((result) {
     340             :       if (result) {
     341           0 :         transaction.commit(queue);
     342             :       } else {
     343           0 :         transaction.reject();
     344             :       }
     345             :       return result;
     346           0 :     }, onError: (Object error) {
     347           0 :       transaction.commit(queue);
     348             :       throw error;
     349             :     });
     350             :   }
     351             : 
     352             :   /// Passes a copy of this queue to [callback], and updates this queue to match
     353             :   /// the copy's position once [callback] completes.
     354             :   ///
     355             :   /// If the returned [CancelableOperation] is canceled, this queue instead
     356             :   /// continues as though [cancelable] hadn't been called. Otherwise, it emits
     357             :   /// the same value or error as [callback].
     358             :   ///
     359             :   /// See also [startTransaction] and [withTransaction].
     360             :   ///
     361             :   /// ```dart
     362             :   /// final _stdinQueue = StreamQueue(stdin);
     363             :   ///
     364             :   /// /// Returns an operation that completes when the user sends a line to
     365             :   /// /// standard input.
     366             :   /// ///
     367             :   /// /// If the operation is canceled, stops waiting for user input.
     368             :   /// CancelableOperation<String> nextStdinLine() =>
     369             :   ///     _stdinQueue.cancelable((queue) => queue.next);
     370             :   /// ```
     371           0 :   CancelableOperation<S> cancelable<S>(
     372             :       Future<S> Function(StreamQueue<T>) callback) {
     373           0 :     var transaction = startTransaction();
     374           0 :     var completer = CancelableCompleter<S>(onCancel: () {
     375           0 :       transaction.reject();
     376             :     });
     377             : 
     378           0 :     var queue = transaction.newQueue();
     379           0 :     completer.complete(callback(queue).whenComplete(() {
     380           0 :       if (!completer.isCanceled) transaction.commit(queue);
     381             :     }));
     382             : 
     383           0 :     return completer.operation;
     384             :   }
     385             : 
     386             :   /// Cancels the underlying event source.
     387             :   ///
     388             :   /// If [immediate] is `false` (the default), the cancel operation waits until
     389             :   /// all previously requested events have been processed, then it cancels the
     390             :   /// subscription providing the events.
     391             :   ///
     392             :   /// If [immediate] is `true`, the source is instead canceled
     393             :   /// immediately. Any pending events are completed as though the underlying
     394             :   /// stream had closed.
     395             :   ///
     396             :   /// The returned future completes with the result of calling
     397             :   /// `cancel`.
     398             :   ///
     399             :   /// After calling `cancel`, no further events can be requested.
     400             :   /// None of [lookAhead], [next], [peek], [rest], [skip], [take] or [cancel]
     401             :   /// may be called again.
     402           0 :   Future? cancel({bool immediate = false}) {
     403           0 :     if (_isClosed) throw _failClosed();
     404           0 :     _isClosed = true;
     405             : 
     406             :     if (!immediate) {
     407           0 :       var request = _CancelRequest<T>(this);
     408           0 :       _addRequest(request);
     409           0 :       return request.future;
     410             :     }
     411             : 
     412           0 :     if (_isDone && _eventQueue.isEmpty) return Future.value();
     413           0 :     return _cancel();
     414             :   }
     415             : 
     416             :   // ------------------------------------------------------------------
     417             :   // Methods that may be called from the request implementations to
     418             :   // control the event stream.
     419             : 
     420             :   /// Matches events with requests.
     421             :   ///
     422             :   /// Called after receiving an event or when the event source closes.
     423             :   ///
     424             :   /// May be called by requests which have returned `false` (saying they
     425             :   /// are not yet done) so they can be checked again before any new
     426             :   /// events arrive.
     427             :   /// Any request returing `false` from `update` when `isDone` is `true`
     428             :   /// *must* call `_updateRequests` when they are ready to continue
     429             :   /// (since no further events will trigger the call).
     430          11 :   void _updateRequests() {
     431          22 :     while (_requestQueue.isNotEmpty) {
     432          55 :       if (_requestQueue.first.update(_eventQueue, _isDone)) {
     433          22 :         _requestQueue.removeFirst();
     434             :       } else {
     435             :         return;
     436             :       }
     437             :     }
     438             : 
     439          11 :     if (!_isDone) {
     440          11 :       _pause();
     441             :     }
     442             :   }
     443             : 
     444             :   /// Extracts a stream from the event source and makes this stream queue
     445             :   /// unusable.
     446             :   ///
     447             :   /// Can only be used by the very last request (the stream queue must
     448             :   /// be closed by that request).
     449             :   /// Only used by [rest].
     450          11 :   Stream<T> _extractStream() {
     451          11 :     assert(_isClosed);
     452          11 :     if (_isDone) {
     453           0 :       return Stream<T>.empty();
     454             :     }
     455          11 :     _isDone = true;
     456             : 
     457          11 :     var subscription = _subscription;
     458             :     if (subscription == null) {
     459           0 :       return _source;
     460             :     }
     461          11 :     _subscription = null;
     462             : 
     463          11 :     var wasPaused = subscription.isPaused;
     464          11 :     var result = SubscriptionStream<T>(subscription);
     465             :     // Resume after creating stream because that pauses the subscription too.
     466             :     // This way there won't be a short resumption in the middle.
     467          11 :     if (wasPaused) subscription.resume();
     468             :     return result;
     469             :   }
     470             : 
     471             :   /// Requests that the event source pauses events.
     472             :   ///
     473             :   /// This is called automatically when the request queue is empty.
     474             :   ///
     475             :   /// The event source is restarted by the next call to [_ensureListening].
     476          11 :   void _pause() {
     477          22 :     _subscription!.pause();
     478             :   }
     479             : 
     480             :   /// Ensures that we are listening on events from the event source.
     481             :   ///
     482             :   /// Starts listening for the first time or resumes after a [_pause].
     483             :   ///
     484             :   /// Is called automatically if a request requires more events.
     485          11 :   void _ensureListening() {
     486          11 :     if (_isDone) return;
     487          11 :     if (_subscription == null) {
     488          44 :       _subscription = _source.listen((data) {
     489          22 :         _addResult(Result.value(data));
     490           0 :       }, onError: (Object error, StackTrace stackTrace) {
     491           0 :         _addResult(Result.error(error, stackTrace));
     492           0 :       }, onDone: () {
     493           0 :         _subscription = null;
     494           0 :         _close();
     495             :       });
     496             :     } else {
     497           0 :       _subscription!.resume();
     498             :     }
     499             :   }
     500             : 
     501             :   /// Cancels the underlying event source.
     502           0 :   Future? _cancel() {
     503           0 :     if (_isDone) return null;
     504           0 :     _subscription ??= _source.listen(null);
     505           0 :     var future = _subscription!.cancel();
     506           0 :     _close();
     507             :     return future;
     508             :   }
     509             : 
     510             :   // ------------------------------------------------------------------
     511             :   // Methods called by the event source to add events or say that it's
     512             :   // done.
     513             : 
     514             :   /// Called when the event source adds a new data or error event.
     515             :   /// Always calls [_updateRequests] after adding.
     516          11 :   void _addResult(Result<T> result) {
     517          22 :     _eventsReceived++;
     518          22 :     _eventQueue.add(result);
     519          11 :     _updateRequests();
     520             :   }
     521             : 
     522             :   /// Called when the event source is done.
     523             :   /// Always calls [_updateRequests] after adding.
     524           0 :   void _close() {
     525           0 :     _isDone = true;
     526           0 :     _updateRequests();
     527             :   }
     528             : 
     529             :   // ------------------------------------------------------------------
     530             :   // Internal helper methods.
     531             : 
     532             :   /// Returns an error for when a request is made after cancel.
     533             :   ///
     534             :   /// Returns a [StateError] with a message saying that either
     535             :   /// [cancel] or [rest] have already been called.
     536           0 :   Error _failClosed() {
     537           0 :     return StateError('Already cancelled');
     538             :   }
     539             : 
     540             :   /// Adds a new request to the queue.
     541             :   ///
     542             :   /// If the request queue is empty and the request can be completed
     543             :   /// immediately, it skips the queue.
     544          11 :   void _addRequest(_EventRequest<T> request) {
     545          22 :     if (_requestQueue.isEmpty) {
     546          33 :       if (request.update(_eventQueue, _isDone)) return;
     547          11 :       _ensureListening();
     548             :     }
     549          22 :     _requestQueue.add(request);
     550             :   }
     551             : }
     552             : 
     553             : /// A transaction on a [StreamQueue], created by [StreamQueue.startTransaction].
     554             : ///
     555             : /// Copies of the parent queue may be created using [newQueue]. Calling [commit]
     556             : /// moves the parent queue to a copy's position, and calling [reject] causes it
     557             : /// to continue as though [StreamQueue.startTransaction] was never called.
     558             : class StreamQueueTransaction<T> {
     559             :   /// The parent queue on which this transaction is active.
     560             :   final StreamQueue<T> _parent;
     561             : 
     562             :   /// The splitter that produces copies of the parent queue's stream.
     563             :   final StreamSplitter<T> _splitter;
     564             : 
     565             :   /// Queues created using [newQueue].
     566             :   final _queues = <StreamQueue>{};
     567             : 
     568             :   /// Whether [commit] has been called.
     569             :   var _committed = false;
     570             : 
     571             :   /// Whether [reject] has been called.
     572             :   var _rejected = false;
     573             : 
     574           0 :   StreamQueueTransaction._(this._parent, Stream<T> source)
     575           0 :       : _splitter = StreamSplitter(source);
     576             : 
     577             :   /// Creates a new copy of the parent queue.
     578             :   ///
     579             :   /// This copy starts at the parent queue's position when
     580             :   /// [StreamQueue.startTransaction] was called. Its position can be committed
     581             :   /// to the parent queue using [commit].
     582           0 :   StreamQueue<T> newQueue() {
     583           0 :     var queue = StreamQueue(_splitter.split());
     584           0 :     _queues.add(queue);
     585             :     return queue;
     586             :   }
     587             : 
     588             :   /// Commits a queue created using [newQueue].
     589             :   ///
     590             :   /// The parent queue's position is updated to be the same as [queue]'s.
     591             :   /// Further requests on all queues created by this transaction, including
     592             :   /// [queue], will complete as though [cancel] were called with `immediate:
     593             :   /// true`.
     594             :   ///
     595             :   /// Throws a [StateError] if [commit] or [reject] have already been called, or
     596             :   /// if there are pending requests on [queue].
     597           0 :   void commit(StreamQueue<T> queue) {
     598           0 :     _assertActive();
     599           0 :     if (!_queues.contains(queue)) {
     600           0 :       throw ArgumentError("Queue doesn't belong to this transaction.");
     601           0 :     } else if (queue._requestQueue.isNotEmpty) {
     602           0 :       throw StateError("A queue with pending requests can't be committed.");
     603             :     }
     604           0 :     _committed = true;
     605             : 
     606             :     // Remove all events from the parent queue that were consumed by the
     607             :     // child queue.
     608           0 :     for (var j = 0; j < queue.eventsDispatched; j++) {
     609           0 :       _parent._eventQueue.removeFirst();
     610             :     }
     611             : 
     612           0 :     _done();
     613             :   }
     614             : 
     615             :   /// Rejects this transaction without updating the parent queue.
     616             :   ///
     617             :   /// The parent will continue as though [StreamQueue.startTransaction] hadn't
     618             :   /// been called. Further requests on all queues created by this transaction
     619             :   /// will complete as though [cancel] were called with `immediate: true`.
     620             :   ///
     621             :   /// Throws a [StateError] if [commit] or [reject] have already been called.
     622           0 :   void reject() {
     623           0 :     _assertActive();
     624           0 :     _rejected = true;
     625           0 :     _done();
     626             :   }
     627             : 
     628             :   // Cancels all [_queues], removes the [_TransactionRequest] from [_parent]'s
     629             :   // request queue, and runs the next request.
     630           0 :   void _done() {
     631           0 :     _splitter.close();
     632           0 :     for (var queue in _queues) {
     633           0 :       queue._cancel();
     634             :     }
     635             :     // If this is the active request in the queue, mark it as finished.
     636           0 :     var currentRequest = _parent._requestQueue.first;
     637           0 :     if (currentRequest is _TransactionRequest &&
     638           0 :         currentRequest.transaction == this) {
     639           0 :       _parent._requestQueue.removeFirst();
     640           0 :       _parent._updateRequests();
     641             :     }
     642             :   }
     643             : 
     644             :   /// Throws a [StateError] if [accept] or [reject] has already been called.
     645           0 :   void _assertActive() {
     646           0 :     if (_committed) {
     647           0 :       throw StateError('This transaction has already been accepted.');
     648           0 :     } else if (_rejected) {
     649           0 :       throw StateError('This transaction has already been rejected.');
     650             :     }
     651             :   }
     652             : }
     653             : 
     654             : /// Request object that receives events when they arrive, until fulfilled.
     655             : ///
     656             : /// Each request that cannot be fulfilled immediately is represented by
     657             : /// an `_EventRequest` object in the request queue.
     658             : ///
     659             : /// Events from the source stream are sent to the first request in the
     660             : /// queue until it reports itself as [isComplete].
     661             : ///
     662             : /// When the first request in the queue `isComplete`, either when becoming
     663             : /// the first request or after receiving an event, its [close] methods is
     664             : /// called.
     665             : ///
     666             : /// The [close] method is also called immediately when the source stream
     667             : /// is done.
     668             : abstract class _EventRequest<T> {
     669             :   /// Handle available events.
     670             :   ///
     671             :   /// The available events are provided as a queue. The `update` function
     672             :   /// should only remove events from the front of the event queue, e.g.,
     673             :   /// using [removeFirst].
     674             :   ///
     675             :   /// Returns `true` if the request is completed, or `false` if it needs
     676             :   /// more events.
     677             :   /// The call may keep events in the queue until the requeust is complete,
     678             :   /// or it may remove them immediately.
     679             :   ///
     680             :   /// If the method returns true, the request is considered fulfilled, and
     681             :   /// will never be called again.
     682             :   ///
     683             :   /// This method is called when a request reaches the front of the request
     684             :   /// queue, and if it returns `false`, it's called again every time a new event
     685             :   /// becomes available, or when the stream closes.
     686             :   /// If the function returns `false` when the stream has already closed
     687             :   /// ([isDone] is true), then the request must call
     688             :   /// [StreamQueue._updateRequests] itself when it's ready to continue.
     689             :   bool update(QueueList<Result<T>> events, bool isDone);
     690             : }
     691             : 
     692             : /// Request for a [StreamQueue.next] call.
     693             : ///
     694             : /// Completes the returned future when receiving the first event,
     695             : /// and is then complete.
     696             : class _NextRequest<T> implements _EventRequest<T> {
     697             :   /// Completer for the future returned by [StreamQueue.next].
     698             :   final _completer = Completer<T>();
     699             : 
     700          11 :   _NextRequest();
     701             : 
     702          33 :   Future<T> get future => _completer.future;
     703             : 
     704          11 :   @override
     705             :   bool update(QueueList<Result<T>> events, bool isDone) {
     706          11 :     if (events.isNotEmpty) {
     707          33 :       events.removeFirst().complete(_completer);
     708             :       return true;
     709             :     }
     710             :     if (isDone) {
     711           0 :       _completer.completeError(StateError('No elements'), StackTrace.current);
     712             :       return true;
     713             :     }
     714             :     return false;
     715             :   }
     716             : }
     717             : 
     718             : /// Request for a [StreamQueue.peek] call.
     719             : ///
     720             : /// Completes the returned future when receiving the first event,
     721             : /// and is then complete, but doesn't consume the event.
     722             : class _PeekRequest<T> implements _EventRequest<T> {
     723             :   /// Completer for the future returned by [StreamQueue.next].
     724             :   final _completer = Completer<T>();
     725             : 
     726           0 :   _PeekRequest();
     727             : 
     728           0 :   Future<T> get future => _completer.future;
     729             : 
     730           0 :   @override
     731             :   bool update(QueueList<Result<T>> events, bool isDone) {
     732           0 :     if (events.isNotEmpty) {
     733           0 :       events.first.complete(_completer);
     734             :       return true;
     735             :     }
     736             :     if (isDone) {
     737           0 :       _completer.completeError(StateError('No elements'), StackTrace.current);
     738             :       return true;
     739             :     }
     740             :     return false;
     741             :   }
     742             : }
     743             : 
     744             : /// Request for a [StreamQueue.skip] call.
     745             : class _SkipRequest<T> implements _EventRequest<T> {
     746             :   /// Completer for the future returned by the skip call.
     747             :   final _completer = Completer<int>();
     748             : 
     749             :   /// Number of remaining events to skip.
     750             :   ///
     751             :   /// The request [isComplete] when the values reaches zero.
     752             :   ///
     753             :   /// Decremented when an event is seen.
     754             :   /// Set to zero when an error is seen since errors abort the skip request.
     755             :   int _eventsToSkip;
     756             : 
     757           0 :   _SkipRequest(this._eventsToSkip);
     758             : 
     759             :   /// The future completed when the correct number of events have been skipped.
     760           0 :   Future<int> get future => _completer.future;
     761             : 
     762           0 :   @override
     763             :   bool update(QueueList<Result<T>> events, bool isDone) {
     764           0 :     while (_eventsToSkip > 0) {
     765           0 :       if (events.isEmpty) {
     766             :         if (isDone) break;
     767             :         return false;
     768             :       }
     769           0 :       _eventsToSkip--;
     770             : 
     771           0 :       var event = events.removeFirst();
     772           0 :       if (event.isError) {
     773           0 :         _completer.completeError(
     774           0 :             event.asError!.error, event.asError!.stackTrace);
     775             :         return true;
     776             :       }
     777             :     }
     778           0 :     _completer.complete(_eventsToSkip);
     779             :     return true;
     780             :   }
     781             : }
     782             : 
     783             : /// Common superclass for [_TakeRequest] and [_LookAheadRequest].
     784             : abstract class _ListRequest<T> implements _EventRequest<T> {
     785             :   /// Completer for the future returned by the take call.
     786             :   final _completer = Completer<List<T>>();
     787             : 
     788             :   /// List collecting events until enough have been seen.
     789             :   final _list = <T>[];
     790             : 
     791             :   /// Number of events to capture.
     792             :   ///
     793             :   /// The request [isComplete] when the length of [_list] reaches
     794             :   /// this value.
     795             :   final int _eventsToTake;
     796             : 
     797           0 :   _ListRequest(this._eventsToTake);
     798             : 
     799             :   /// The future completed when the correct number of events have been captured.
     800           0 :   Future<List<T>> get future => _completer.future;
     801             : }
     802             : 
     803             : /// Request for a [StreamQueue.take] call.
     804             : class _TakeRequest<T> extends _ListRequest<T> {
     805           0 :   _TakeRequest(int eventsToTake) : super(eventsToTake);
     806             : 
     807           0 :   @override
     808             :   bool update(QueueList<Result<T>> events, bool isDone) {
     809           0 :     while (_list.length < _eventsToTake) {
     810           0 :       if (events.isEmpty) {
     811             :         if (isDone) break;
     812             :         return false;
     813             :       }
     814             : 
     815           0 :       var event = events.removeFirst();
     816           0 :       if (event.isError) {
     817           0 :         event.asError!.complete(_completer);
     818             :         return true;
     819             :       }
     820           0 :       _list.add(event.asValue!.value);
     821             :     }
     822           0 :     _completer.complete(_list);
     823             :     return true;
     824             :   }
     825             : }
     826             : 
     827             : /// Request for a [StreamQueue.lookAhead] call.
     828             : class _LookAheadRequest<T> extends _ListRequest<T> {
     829           0 :   _LookAheadRequest(int eventsToTake) : super(eventsToTake);
     830             : 
     831           0 :   @override
     832             :   bool update(QueueList<Result<T>> events, bool isDone) {
     833           0 :     while (_list.length < _eventsToTake) {
     834           0 :       if (events.length == _list.length) {
     835             :         if (isDone) break;
     836             :         return false;
     837             :       }
     838           0 :       var event = events.elementAt(_list.length);
     839           0 :       if (event.isError) {
     840           0 :         event.asError!.complete(_completer);
     841             :         return true;
     842             :       }
     843           0 :       _list.add(event.asValue!.value);
     844             :     }
     845           0 :     _completer.complete(_list);
     846             :     return true;
     847             :   }
     848             : }
     849             : 
     850             : /// Request for a [StreamQueue.cancel] call.
     851             : ///
     852             : /// The request needs no events, it just waits in the request queue
     853             : /// until all previous events are fulfilled, then it cancels the stream queue
     854             : /// source subscription.
     855             : class _CancelRequest<T> implements _EventRequest<T> {
     856             :   /// Completer for the future returned by the `cancel` call.
     857             :   final _completer = Completer<void>();
     858             : 
     859             :   /// When the event is completed, it needs to cancel the active subscription
     860             :   /// of the `StreamQueue` object, if any.
     861             :   final StreamQueue _streamQueue;
     862             : 
     863           0 :   _CancelRequest(this._streamQueue);
     864             : 
     865             :   /// The future completed when the cancel request is completed.
     866           0 :   Future get future => _completer.future;
     867             : 
     868           0 :   @override
     869             :   bool update(QueueList<Result<T>> events, bool isDone) {
     870           0 :     if (_streamQueue._isDone) {
     871           0 :       _completer.complete();
     872             :     } else {
     873           0 :       _streamQueue._ensureListening();
     874           0 :       _completer.complete(_streamQueue._extractStream().listen(null).cancel());
     875             :     }
     876             :     return true;
     877             :   }
     878             : }
     879             : 
     880             : /// Request for a [StreamQueue.rest] call.
     881             : ///
     882             : /// The request is always complete, it just waits in the request queue
     883             : /// until all previous events are fulfilled, then it takes over the
     884             : /// stream events subscription and creates a stream from it.
     885             : class _RestRequest<T> implements _EventRequest<T> {
     886             :   /// Completer for the stream returned by the `rest` call.
     887             :   final _completer = StreamCompleter<T>();
     888             : 
     889             :   /// The [StreamQueue] object that has this request queued.
     890             :   ///
     891             :   /// When the event is completed, it needs to cancel the active subscription
     892             :   /// of the `StreamQueue` object, if any.
     893             :   final StreamQueue<T> _streamQueue;
     894             : 
     895          11 :   _RestRequest(this._streamQueue);
     896             : 
     897             :   /// The stream which will contain the remaining events of [_streamQueue].
     898          33 :   Stream<T> get stream => _completer.stream;
     899             : 
     900          11 :   @override
     901             :   bool update(QueueList<Result<T>> events, bool isDone) {
     902          11 :     if (events.isEmpty) {
     903          22 :       if (_streamQueue._isDone) {
     904           0 :         _completer.setEmpty();
     905             :       } else {
     906          44 :         _completer.setSourceStream(_streamQueue._extractStream());
     907             :       }
     908             :     } else {
     909             :       // There are prefetched events which needs to be added before the
     910             :       // remaining stream.
     911           0 :       var controller = StreamController<T>();
     912           0 :       for (var event in events) {
     913           0 :         event.addTo(controller);
     914             :       }
     915             :       controller
     916           0 :           .addStream(_streamQueue._extractStream(), cancelOnError: false)
     917           0 :           .whenComplete(controller.close);
     918           0 :       _completer.setSourceStream(controller.stream);
     919             :     }
     920             :     return true;
     921             :   }
     922             : }
     923             : 
     924             : /// Request for a [StreamQueue.hasNext] call.
     925             : ///
     926             : /// Completes the [future] with `true` if it sees any event,
     927             : /// but doesn't consume the event.
     928             : /// If the request is closed without seeing an event, then
     929             : /// the [future] is completed with `false`.
     930             : class _HasNextRequest<T> implements _EventRequest<T> {
     931             :   final _completer = Completer<bool>();
     932             : 
     933           0 :   Future<bool> get future => _completer.future;
     934             : 
     935           0 :   @override
     936             :   bool update(QueueList<Result<T>> events, bool isDone) {
     937           0 :     if (events.isNotEmpty) {
     938           0 :       _completer.complete(true);
     939             :       return true;
     940             :     }
     941             :     if (isDone) {
     942           0 :       _completer.complete(false);
     943             :       return true;
     944             :     }
     945             :     return false;
     946             :   }
     947             : }
     948             : 
     949             : /// Request for a [StreamQueue.startTransaction] call.
     950             : ///
     951             : /// This request isn't complete until the user calls
     952             : /// [StreamQueueTransaction.commit] or [StreamQueueTransaction.reject], at which
     953             : /// point it manually removes itself from the request queue and calls
     954             : /// [StreamQueue._updateRequests].
     955             : class _TransactionRequest<T> implements _EventRequest<T> {
     956             :   /// The transaction created by this request.
     957             :   late final StreamQueueTransaction<T> transaction;
     958             : 
     959             :   /// The controller that passes events to [transaction].
     960             :   final _controller = StreamController<T>(sync: true);
     961             : 
     962             :   /// The number of events passed to [_controller] so far.
     963             :   var _eventsSent = 0;
     964             : 
     965           0 :   _TransactionRequest(StreamQueue<T> parent) {
     966           0 :     transaction = StreamQueueTransaction._(parent, _controller.stream);
     967             :   }
     968             : 
     969           0 :   @override
     970             :   bool update(QueueList<Result<T>> events, bool isDone) {
     971           0 :     while (_eventsSent < events.length) {
     972           0 :       events[_eventsSent++].addTo(_controller);
     973             :     }
     974           0 :     if (isDone && !_controller.isClosed) _controller.close();
     975           0 :     return transaction._committed || transaction._rejected;
     976             :   }
     977             : }

Generated by: LCOV version 1.14