| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of dart.async; |
| |
| /// Wraps an [_EventSink] so it exposes only the [EventSink] interface. |
| class _EventSinkWrapper<T> implements EventSink<T> { |
| _EventSink<T> _sink; |
| _EventSinkWrapper(this._sink); |
| |
| void add(T data) { |
| _sink._add(data); |
| } |
| |
| void addError(Object error, [StackTrace? stackTrace]) { |
| _sink._addError(error, stackTrace ?? AsyncError.defaultStackTrace(error)); |
| } |
| |
| void close() { |
| _sink._close(); |
| } |
| } |
| |
| /// A StreamSubscription that pipes data through a sink. |
| /// |
| /// The constructor of this class takes a [_SinkMapper] which maps from |
| /// [EventSink] to [EventSink]. The input to the mapper is the output of |
| /// the transformation. The returned sink is the transformation's input. |
| class _SinkTransformerStreamSubscription<S, T> |
| extends _BufferingStreamSubscription<T> { |
| /// The transformer's input sink. |
| late EventSink<S> _transformerSink; |
| |
| /// The subscription to the input stream. |
| StreamSubscription<S>? _subscription; |
| |
| _SinkTransformerStreamSubscription( |
| Stream<S> source, |
| _SinkMapper<S, T> mapper, |
| void onData(T data)?, |
| Function? onError, |
| void onDone()?, |
| bool cancelOnError) |
| // We set the adapter's target only when the user is allowed to send data. |
| : super(onData, onError, onDone, cancelOnError) { |
| _transformerSink = mapper(_EventSinkWrapper<T>(this)); |
| _subscription = |
| source.listen(_handleData, onError: _handleError, onDone: _handleDone); |
| } |
| |
| // _EventSink interface. |
| |
| /// Adds an event to this subscriptions. |
| /// |
| /// Contrary to normal [_BufferingStreamSubscription]s we may receive |
| /// events when the stream is already closed. Report them as state |
| /// error. |
| void _add(T data) { |
| if (_isClosed) { |
| throw StateError("Stream is already closed"); |
| } |
| super._add(data); |
| } |
| |
| /// Adds an error event to this subscriptions. |
| /// |
| /// Contrary to normal [_BufferingStreamSubscription]s we may receive |
| /// events when the stream is already closed. Report them as state |
| /// error. |
| void _addError(Object error, StackTrace stackTrace) { |
| if (_isClosed) { |
| throw new StateError("Stream is already closed"); |
| } |
| super._addError(error, stackTrace); |
| } |
| |
| /// Adds a close event to this subscriptions. |
| /// |
| /// Contrary to normal [_BufferingStreamSubscription]s we may receive |
| /// events when the stream is already closed. Report them as state |
| /// error. |
| void _close() { |
| if (_isClosed) { |
| throw new StateError("Stream is already closed"); |
| } |
| super._close(); |
| } |
| |
| // _BufferingStreamSubscription hooks. |
| |
| void _onPause() { |
| _subscription?.pause(); |
| } |
| |
| void _onResume() { |
| _subscription?.resume(); |
| } |
| |
| Future<void>? _onCancel() { |
| var subscription = _subscription; |
| if (subscription != null) { |
| _subscription = null; |
| return subscription.cancel(); |
| } |
| return null; |
| } |
| |
| void _handleData(S data) { |
| try { |
| _transformerSink.add(data); |
| } catch (e, s) { |
| _addError(e, s); |
| } |
| } |
| |
| void _handleError(Object error, StackTrace stackTrace) { |
| try { |
| _transformerSink.addError(error, stackTrace); |
| } catch (e, s) { |
| if (identical(e, error)) { |
| _addError(error, stackTrace); |
| } else { |
| _addError(e, s); |
| } |
| } |
| } |
| |
| void _handleDone() { |
| try { |
| _subscription = null; |
| _transformerSink.close(); |
| } catch (e, s) { |
| _addError(e, s); |
| } |
| } |
| } |
| |
| typedef EventSink<S> _SinkMapper<S, T>(EventSink<T> output); |
| |
| /// A [StreamTransformer] for [Sink]-mappers. |
| /// |
| /// A Sink-mapper takes an [EventSink] (its output) and returns another |
| /// [EventSink] (its input). |
| /// |
| /// Note that this class can be `const`. |
| class _StreamSinkTransformer<S, T> extends StreamTransformerBase<S, T> { |
| final _SinkMapper<S, T> _sinkMapper; |
| const _StreamSinkTransformer(this._sinkMapper); |
| |
| Stream<T> bind(Stream<S> stream) => |
| new _BoundSinkStream<S, T>(stream, _sinkMapper); |
| } |
| |
| /// The result of binding a [StreamTransformer] for [Sink]-mappers. |
| /// |
| /// It contains the bound Stream and the sink-mapper. Only when the user starts |
| /// listening to this stream is the sink-mapper invoked. The result is used |
| /// to create a StreamSubscription that transforms events. |
| class _BoundSinkStream<S, T> extends Stream<T> { |
| final _SinkMapper<S, T> _sinkMapper; |
| final Stream<S> _stream; |
| |
| bool get isBroadcast => _stream.isBroadcast; |
| |
| _BoundSinkStream(this._stream, this._sinkMapper); |
| |
| StreamSubscription<T> listen(void onData(T event)?, |
| {Function? onError, void onDone()?, bool? cancelOnError}) { |
| StreamSubscription<T> subscription = |
| _SinkTransformerStreamSubscription<S, T>(_stream, _sinkMapper, onData, |
| onError, onDone, cancelOnError ?? false); |
| return subscription; |
| } |
| } |
| |
| /// Data-handler coming from [StreamTransformer.fromHandlers]. |
| typedef void _TransformDataHandler<S, T>(S data, EventSink<T> sink); |
| |
| /// Error-handler coming from [StreamTransformer.fromHandlers]. |
| typedef void _TransformErrorHandler<T>( |
| Object error, StackTrace stackTrace, EventSink<T> sink); |
| |
| /// Done-handler coming from [StreamTransformer.fromHandlers]. |
| typedef void _TransformDoneHandler<T>(EventSink<T> sink); |
| |
| /// Wraps handlers (from [StreamTransformer.fromHandlers]) into an `EventSink`. |
| /// |
| /// This way we can reuse the code from [_StreamSinkTransformer]. |
| class _HandlerEventSink<S, T> implements EventSink<S> { |
| final _TransformDataHandler<S, T>? _handleData; |
| final _TransformErrorHandler<T>? _handleError; |
| final _TransformDoneHandler<T>? _handleDone; |
| |
| /// The output sink where the handlers should send their data into. |
| /// Set to `null` when closed. |
| EventSink<T>? _sink; |
| |
| _HandlerEventSink(this._handleData, this._handleError, this._handleDone, |
| EventSink<T> this._sink); |
| |
| void add(S data) { |
| var sink = _sink; |
| if (sink == null) { |
| throw StateError("Sink is closed"); |
| } |
| var handleData = _handleData; |
| if (handleData != null) { |
| handleData(data, sink); |
| } else { |
| sink.add(data as T); |
| } |
| } |
| |
| void addError(Object error, [StackTrace? stackTrace]) { |
| checkNotNullable(error, "error"); |
| var sink = _sink; |
| if (sink == null) { |
| throw StateError("Sink is closed"); |
| } |
| var handleError = _handleError; |
| stackTrace ??= AsyncError.defaultStackTrace(error); |
| if (handleError != null) { |
| handleError(error, stackTrace, sink); |
| } else { |
| sink.addError(error, stackTrace); |
| } |
| } |
| |
| void close() { |
| var sink = _sink; |
| if (sink == null) return; |
| _sink = null; |
| var handleDone = _handleDone; |
| if (handleDone != null) { |
| handleDone(sink); |
| } else { |
| sink.close(); |
| } |
| } |
| } |
| |
| /// A StreamTransformer that transformers events with the given handlers. |
| /// |
| /// Note that this transformer can only be used once. |
| class _StreamHandlerTransformer<S, T> extends _StreamSinkTransformer<S, T> { |
| _StreamHandlerTransformer( |
| {void handleData(S data, EventSink<T> sink)?, |
| void handleError(Object error, StackTrace stackTrace, EventSink<T> sink)?, |
| void handleDone(EventSink<T> sink)?}) |
| : super((EventSink<T> outputSink) { |
| return new _HandlerEventSink<S, T>( |
| handleData, handleError, handleDone, outputSink); |
| }); |
| |
| Stream<T> bind(Stream<S> stream) { |
| return super.bind(stream); |
| } |
| } |
| |
| /// A StreamTransformer that overrides [StreamTransformer.bind] with a callback. |
| class _StreamBindTransformer<S, T> extends StreamTransformerBase<S, T> { |
| final Stream<T> Function(Stream<S>) _bind; |
| _StreamBindTransformer(this._bind); |
| |
| Stream<T> bind(Stream<S> stream) => _bind(stream); |
| } |
| |
| /// A closure mapping a stream and cancelOnError to a StreamSubscription. |
| typedef StreamSubscription<T> _SubscriptionTransformer<S, T>( |
| Stream<S> stream, bool cancelOnError); |
| |
| /// A [StreamTransformer] that minimizes the number of additional classes. |
| /// |
| /// Instead of implementing three classes: a [StreamTransformer], a [Stream] |
| /// (as the result of a `bind` call) and a [StreamSubscription] (which does the |
| /// actual work), this class only requires a function that is invoked when the |
| /// last bit (the subscription) of the transformer-workflow is needed. |
| /// |
| /// The given transformer function maps from Stream and cancelOnError to a |
| /// `StreamSubscription`. As such it can also act on `cancel` events, making it |
| /// fully general. |
| class _StreamSubscriptionTransformer<S, T> extends StreamTransformerBase<S, T> { |
| final _SubscriptionTransformer<S, T> _onListen; |
| |
| const _StreamSubscriptionTransformer(this._onListen); |
| |
| Stream<T> bind(Stream<S> stream) => |
| new _BoundSubscriptionStream<S, T>(stream, _onListen); |
| } |
| |
| /// A stream transformed by a [_StreamSubscriptionTransformer]. |
| /// |
| /// When this stream is listened to it invokes the [_onListen] function with |
| /// the stored [_stream]. Usually the transformer starts listening at this |
| /// moment. |
| class _BoundSubscriptionStream<S, T> extends Stream<T> { |
| final _SubscriptionTransformer<S, T> _onListen; |
| final Stream<S> _stream; |
| |
| bool get isBroadcast => _stream.isBroadcast; |
| |
| _BoundSubscriptionStream(this._stream, this._onListen); |
| |
| StreamSubscription<T> listen(void onData(T event)?, |
| {Function? onError, void onDone()?, bool? cancelOnError}) { |
| StreamSubscription<T> result = _onListen(_stream, cancelOnError ?? false); |
| result.onData(onData); |
| result.onError(onError); |
| result.onDone(onDone); |
| return result; |
| } |
| } |