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 'async_memoizer.dart'; 8 : 9 : typedef _AsyncHandler<T> = Future Function(StreamSubscription<T> inner); 10 : 11 : typedef _VoidHandler<T> = void Function(StreamSubscription<T> inner); 12 : 13 : /// Creates a [StreamTransformer] that modifies the behavior of subscriptions to 14 : /// a stream. 15 : /// 16 : /// When [StreamSubscription.cancel], [StreamSubscription.pause], or 17 : /// [StreamSubscription.resume] is called, the corresponding handler is invoked. 18 : /// By default, handlers just forward to the underlying subscription. 19 : /// 20 : /// Guarantees that none of the [StreamSubscription] callbacks and none of the 21 : /// callbacks passed to `subscriptionTransformer()` will be invoked once the 22 : /// transformed [StreamSubscription] has been canceled and `handleCancel()` has 23 : /// run. The [handlePause] and [handleResume] are invoked regardless of whether 24 : /// the subscription is paused already or not. 25 : /// 26 : /// In order to preserve [StreamSubscription] guarantees, **all callbacks must 27 : /// synchronously call the corresponding method** on the inner 28 : /// [StreamSubscription]: [handleCancel] must call `cancel()`, [handlePause] 29 : /// must call `pause()`, and [handleResume] must call `resume()`. 30 0 : StreamTransformer<T, T> subscriptionTransformer<T>( 31 : {Future Function(StreamSubscription<T>)? handleCancel, 32 : void Function(StreamSubscription<T>)? handlePause, 33 : void Function(StreamSubscription<T>)? handleResume}) { 34 0 : return StreamTransformer((stream, cancelOnError) { 35 0 : return _TransformedSubscription( 36 0 : stream.listen(null, cancelOnError: cancelOnError), 37 0 : handleCancel ?? (inner) => inner.cancel(), 38 : handlePause ?? 39 0 : (inner) { 40 0 : inner.pause(); 41 : }, 42 : handleResume ?? 43 0 : (inner) { 44 0 : inner.resume(); 45 : }); 46 : }); 47 : } 48 : 49 : /// A [StreamSubscription] wrapper that calls callbacks for subscription 50 : /// methods. 51 : class _TransformedSubscription<T> implements StreamSubscription<T> { 52 : /// The wrapped subscription. 53 : StreamSubscription<T>? _inner; 54 : 55 : /// The callback to run when [cancel] is called. 56 : final _AsyncHandler<T> _handleCancel; 57 : 58 : /// The callback to run when [pause] is called. 59 : final _VoidHandler<T> _handlePause; 60 : 61 : /// The callback to run when [resume] is called. 62 : final _VoidHandler<T> _handleResume; 63 : 64 0 : @override 65 0 : bool get isPaused => _inner?.isPaused ?? false; 66 : 67 0 : _TransformedSubscription( 68 : this._inner, this._handleCancel, this._handlePause, this._handleResume); 69 : 70 0 : @override 71 : void onData(void Function(T)? handleData) { 72 0 : _inner?.onData(handleData); 73 : } 74 : 75 0 : @override 76 : void onError(Function? handleError) { 77 0 : _inner?.onError(handleError); 78 : } 79 : 80 0 : @override 81 : void onDone(void Function()? handleDone) { 82 0 : _inner?.onDone(handleDone); 83 : } 84 : 85 0 : @override 86 0 : Future cancel() => _cancelMemoizer.runOnce(() { 87 0 : var inner = _inner!; 88 0 : inner.onData(null); 89 0 : inner.onDone(null); 90 : 91 : // Setting onError to null will cause errors to be top-leveled. 92 0 : inner.onError((_, __) {}); 93 0 : _inner = null; 94 0 : return _handleCancel(inner); 95 : }); 96 : final _cancelMemoizer = AsyncMemoizer(); 97 : 98 0 : @override 99 : void pause([Future? resumeFuture]) { 100 0 : if (_cancelMemoizer.hasRun) return; 101 0 : if (resumeFuture != null) resumeFuture.whenComplete(resume); 102 0 : _handlePause(_inner!); 103 : } 104 : 105 0 : @override 106 : void resume() { 107 0 : if (_cancelMemoizer.hasRun) return; 108 0 : _handleResume(_inner!); 109 : } 110 : 111 0 : @override 112 : Future<E> asFuture<E>([E? futureValue]) => 113 0 : _inner?.asFuture(futureValue) ?? Completer<E>().future; 114 : }