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 : 7 : /// A single-subscription [stream] where the contents are provided later. 8 : /// 9 : /// It is generally recommended that you never create a `Future<Stream>` 10 : /// because you can just directly create a stream that doesn't do anything 11 : /// until it's ready to do so. 12 : /// This class can be used to create such a stream. 13 : /// 14 : /// The [stream] is a normal stream that you can listen to immediately, 15 : /// but until either [setSourceStream] or [setEmpty] is called, 16 : /// the stream won't produce any events. 17 : /// 18 : /// The same effect can be achieved by using a [StreamController] 19 : /// and adding the stream using `addStream` when both 20 : /// the controller's stream is listened to and the source stream is ready. 21 : /// This class attempts to shortcut some of the overhead when possible. 22 : /// For example, if the [stream] is only listened to 23 : /// after the source stream has been set, 24 : /// the listen is performed directly on the source stream. 25 : class StreamCompleter<T> { 26 : /// The stream doing the actual work, is returned by [stream]. 27 : final _stream = _CompleterStream<T>(); 28 : 29 : /// Convert a `Future<Stream>` to a `Stream`. 30 : /// 31 : /// This creates a stream using a stream completer, 32 : /// and sets the source stream to the result of the future when the 33 : /// future completes. 34 : /// 35 : /// If the future completes with an error, the returned stream will 36 : /// instead contain just that error. 37 0 : static Stream<T> fromFuture<T>(Future<Stream<T>> streamFuture) { 38 0 : var completer = StreamCompleter<T>(); 39 0 : streamFuture.then(completer.setSourceStream, onError: completer.setError); 40 0 : return completer.stream; 41 : } 42 : 43 : /// The stream of this completer. 44 : /// 45 : /// This stream is always a single-subscription stream. 46 : /// 47 : /// When a source stream is provided, its events will be forwarded to 48 : /// listeners on this stream. 49 : /// 50 : /// The stream can be listened either before or after a source stream 51 : /// is set. 52 22 : Stream<T> get stream => _stream; 53 : 54 : /// Set a stream as the source of events for the [StreamCompleter]'s 55 : /// [stream]. 56 : /// 57 : /// The completer's `stream` will act exactly as [sourceStream]. 58 : /// 59 : /// If the source stream is set before [stream] is listened to, 60 : /// the listen call on [stream] is forwarded directly to [sourceStream]. 61 : /// 62 : /// If [stream] is listened to before setting the source stream, 63 : /// an intermediate subscription is created. It looks like a completely 64 : /// normal subscription, and can be paused or canceled, but it won't 65 : /// produce any events until a source stream is provided. 66 : /// 67 : /// If the `stream` subscription is canceled before a source stream is set, 68 : /// the source stream will be listened to and immediately canceled again. 69 : /// 70 : /// Otherwise, when the source stream is then set, 71 : /// it is immediately listened to, and its events are forwarded to the 72 : /// existing subscription. 73 : /// 74 : /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at 75 : /// most once. Trying to call any of them again will fail. 76 11 : void setSourceStream(Stream<T> sourceStream) { 77 22 : if (_stream._isSourceStreamSet) { 78 0 : throw StateError('Source stream already set'); 79 : } 80 22 : _stream._setSourceStream(sourceStream); 81 : } 82 : 83 : /// Equivalent to setting an empty stream using [setSourceStream]. 84 : /// 85 : /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at 86 : /// most once. Trying to call any of them again will fail. 87 0 : void setEmpty() { 88 0 : if (_stream._isSourceStreamSet) { 89 0 : throw StateError('Source stream already set'); 90 : } 91 0 : _stream._setEmpty(); 92 : } 93 : 94 : /// Completes this to a stream that emits [error] and then closes. 95 : /// 96 : /// This is useful when the process of creating the data for the stream fails. 97 : /// 98 : /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at 99 : /// most once. Trying to call any of them again will fail. 100 0 : void setError(Object error, [StackTrace? stackTrace]) { 101 0 : setSourceStream(Stream.fromFuture(Future.error(error, stackTrace))); 102 : } 103 : } 104 : 105 : /// Stream completed by [StreamCompleter]. 106 : class _CompleterStream<T> extends Stream<T> { 107 : /// Controller for an intermediate stream. 108 : /// 109 : /// Created if the user listens on this stream before the source stream 110 : /// is set, or if using [_setEmpty] so there is no source stream. 111 : StreamController<T>? _controller; 112 : 113 : /// Source stream for the events provided by this stream. 114 : /// 115 : /// Set when the completer sets the source stream using [_setSourceStream] 116 : /// or [_setEmpty]. 117 : Stream<T>? _sourceStream; 118 : 119 11 : @override 120 : StreamSubscription<T> listen(void Function(T)? onData, 121 : {Function? onError, void Function()? onDone, bool? cancelOnError}) { 122 11 : if (_controller == null) { 123 11 : var sourceStream = _sourceStream; 124 11 : if (sourceStream != null && !sourceStream.isBroadcast) { 125 : // If the source stream is itself single subscription, 126 : // just listen to it directly instead of creating a controller. 127 11 : return sourceStream.listen(onData, 128 : onError: onError, onDone: onDone, cancelOnError: cancelOnError); 129 : } 130 0 : _ensureController(); 131 0 : if (_sourceStream != null) { 132 0 : _linkStreamToController(); 133 : } 134 : } 135 0 : return _controller!.stream.listen(onData, 136 : onError: onError, onDone: onDone, cancelOnError: cancelOnError); 137 : } 138 : 139 : /// Whether a source stream has been set. 140 : /// 141 : /// Used to throw an error if trying to set a source stream twice. 142 22 : bool get _isSourceStreamSet => _sourceStream != null; 143 : 144 : /// Sets the source stream providing the events for this stream. 145 : /// 146 : /// If set before the user listens, listen calls will be directed directly 147 : /// to the source stream. If the user listenes earlier, and intermediate 148 : /// stream is created using a stream controller, and the source stream is 149 : /// linked into that stream later. 150 11 : void _setSourceStream(Stream<T> sourceStream) { 151 11 : assert(_sourceStream == null); 152 11 : _sourceStream = sourceStream; 153 11 : if (_controller != null) { 154 : // User has already listened, so provide the data through controller. 155 0 : _linkStreamToController(); 156 : } 157 : } 158 : 159 : /// Links source stream to controller when both are available. 160 0 : void _linkStreamToController() { 161 0 : var controller = _controller!; 162 : controller 163 0 : .addStream(_sourceStream!, cancelOnError: false) 164 0 : .whenComplete(controller.close); 165 : } 166 : 167 : /// Sets an empty source stream. 168 : /// 169 : /// Uses [_controller] for the stream, then closes the controller 170 : /// immediately. 171 0 : void _setEmpty() { 172 0 : assert(_sourceStream == null); 173 0 : var controller = _ensureController(); 174 0 : _sourceStream = controller.stream; // Mark stream as set. 175 0 : controller.close(); 176 : } 177 : 178 : // Creates the [_controller]. 179 0 : StreamController<T> _ensureController() { 180 0 : return _controller ??= StreamController<T>(sync: true); 181 : } 182 : }