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 'null_stream_sink.dart'; 8 : 9 : /// A [sink] where the destination is provided later. 10 : /// 11 : /// The [sink] is a normal sink that you can add events to to immediately, but 12 : /// until [setDestinationSink] is called, the events will be buffered. 13 : /// 14 : /// The same effect can be achieved by using a [StreamController] and adding it 15 : /// to the sink using [Sink.addStream] when the destination sink is ready. This 16 : /// class attempts to shortcut some of the overhead when possible. For example, 17 : /// if the [sink] only has events added after the destination sink has been set, 18 : /// those events are added directly to the sink. 19 : class StreamSinkCompleter<T> { 20 : /// The sink for this completer. 21 : /// 22 : /// When a destination sink is provided, events that have been passed to the 23 : /// sink will be forwarded to the destination. 24 : /// 25 : /// Events can be added to the sink either before or after a destination sink 26 : /// is set. 27 : final StreamSink<T> sink = _CompleterSink<T>(); 28 : 29 : /// Returns [sink] typed as a [_CompleterSink]. 30 0 : _CompleterSink<T> get _sink => sink as _CompleterSink<T>; 31 : 32 : /// Convert a `Future<StreamSink>` to a `StreamSink`. 33 : /// 34 : /// This creates a sink using a sink completer, and sets the destination sink 35 : /// to the result of the future when the future completes. 36 : /// 37 : /// If the future completes with an error, the returned sink will instead 38 : /// be closed. Its [Sink.done] future will contain the error. 39 0 : static StreamSink<T> fromFuture<T>(Future<StreamSink<T>> sinkFuture) { 40 0 : var completer = StreamSinkCompleter<T>(); 41 0 : sinkFuture.then(completer.setDestinationSink, onError: completer.setError); 42 0 : return completer.sink; 43 : } 44 : 45 : /// Sets a sink as the destination for events from the [StreamSinkCompleter]'s 46 : /// [sink]. 47 : /// 48 : /// The completer's [sink] will act exactly as [destinationSink]. 49 : /// 50 : /// If the destination sink is set before events are added to [sink], further 51 : /// events are forwarded directly to [destinationSink]. 52 : /// 53 : /// If events are added to [sink] before setting the destination sink, they're 54 : /// buffered until the destination is available. 55 : /// 56 : /// A destination sink may be set at most once. 57 : /// 58 : /// Either of [setDestinationSink] or [setError] may be called at most once. 59 : /// Trying to call either of them again will fail. 60 0 : void setDestinationSink(StreamSink<T> destinationSink) { 61 0 : if (_sink._destinationSink != null) { 62 0 : throw StateError('Destination sink already set'); 63 : } 64 0 : _sink._setDestinationSink(destinationSink); 65 : } 66 : 67 : /// Completes this to a closed sink whose [Sink.done] future emits [error]. 68 : /// 69 : /// This is useful when the process of loading the sink fails. 70 : /// 71 : /// Either of [setDestinationSink] or [setError] may be called at most once. 72 : /// Trying to call either of them again will fail. 73 0 : void setError(Object error, [StackTrace? stackTrace]) { 74 0 : setDestinationSink(NullStreamSink.error(error, stackTrace)); 75 : } 76 : } 77 : 78 : /// [StreamSink] completed by [StreamSinkCompleter]. 79 : class _CompleterSink<T> implements StreamSink<T> { 80 : /// Controller for an intermediate sink. 81 : /// 82 : /// Created if the user adds events to this sink before the destination sink 83 : /// is set. 84 : StreamController<T>? _controller; 85 : 86 : /// Completer for [done]. 87 : /// 88 : /// Created if the user requests the [done] future before the destination sink 89 : /// is set. 90 : Completer? _doneCompleter; 91 : 92 : /// Destination sink for the events added to this sink. 93 : /// 94 : /// Set when [StreamSinkCompleter.setDestinationSink] is called. 95 : StreamSink<T>? _destinationSink; 96 : 97 : /// Whether events should be sent directly to [_destinationSink], as opposed 98 : /// to going through [_controller]. 99 0 : bool get _canSendDirectly => _controller == null && _destinationSink != null; 100 : 101 0 : @override 102 : Future get done { 103 0 : if (_doneCompleter != null) return _doneCompleter!.future; 104 0 : if (_destinationSink == null) { 105 0 : _doneCompleter = Completer.sync(); 106 0 : return _doneCompleter!.future; 107 : } 108 0 : return _destinationSink!.done; 109 : } 110 : 111 0 : @override 112 : void add(T event) { 113 0 : if (_canSendDirectly) { 114 0 : _destinationSink!.add(event); 115 : } else { 116 0 : _ensureController().add(event); 117 : } 118 : } 119 : 120 0 : @override 121 : void addError(error, [StackTrace? stackTrace]) { 122 0 : if (_canSendDirectly) { 123 0 : _destinationSink!.addError(error, stackTrace); 124 : } else { 125 0 : _ensureController().addError(error, stackTrace); 126 : } 127 : } 128 : 129 0 : @override 130 : Future addStream(Stream<T> stream) { 131 0 : if (_canSendDirectly) return _destinationSink!.addStream(stream); 132 : 133 0 : return _ensureController().addStream(stream, cancelOnError: false); 134 : } 135 : 136 0 : @override 137 : Future close() { 138 0 : if (_canSendDirectly) { 139 0 : _destinationSink!.close(); 140 : } else { 141 0 : _ensureController().close(); 142 : } 143 0 : return done; 144 : } 145 : 146 : /// Create [_controller] if it doesn't yet exist. 147 0 : StreamController<T> _ensureController() { 148 0 : return _controller ??= StreamController(sync: true); 149 : } 150 : 151 : /// Sets the destination sink to which events from this sink will be provided. 152 : /// 153 : /// If set before the user adds events, events will be added directly to the 154 : /// destination sink. If the user adds events earlier, an intermediate sink is 155 : /// created using a stream controller, and the destination sink is linked to 156 : /// it later. 157 0 : void _setDestinationSink(StreamSink<T> sink) { 158 0 : assert(_destinationSink == null); 159 0 : _destinationSink = sink; 160 : 161 : // If the user has already added data, it's buffered in the controller, so 162 : // we add it to the sink. 163 0 : if (_controller != null) { 164 : // Catch any error that may come from [addStream] or [sink.close]. They'll 165 : // be reported through [done] anyway. 166 : sink 167 0 : .addStream(_controller!.stream) 168 0 : .whenComplete(sink.close) 169 0 : .catchError((_) {}); 170 : } 171 : 172 : // If the user has already asked when the sink is done, connect the sink's 173 : // done callback to that completer. 174 0 : if (_doneCompleter != null) { 175 0 : _doneCompleter!.complete(sink.done); 176 : } 177 : } 178 : }