// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; namespace System.Reactive.Linq.ObservableImpl { internal sealed class Collect : PushToPullAdapter { private readonly Func _getInitialCollector; private readonly Func _merge; private readonly Func _getNewCollector; public Collect(IObservable source, Func getInitialCollector, Func merge, Func getNewCollector) : base(source) { _getInitialCollector = getInitialCollector; _merge = merge; _getNewCollector = getNewCollector; } protected override PushToPullSink Run() => new _(_merge, _getNewCollector, _getInitialCollector()); private sealed class _ : PushToPullSink { private readonly object _gate; private readonly Func _merge; private readonly Func _getNewCollector; public _(Func merge, Func getNewCollector, TResult collector) { _gate = new object(); _merge = merge; _getNewCollector = getNewCollector; _collector = collector; } private TResult _collector; private Exception? _error; private bool _hasCompleted; private bool _done; public override void OnNext(TSource value) { lock (_gate) { try { _collector = _merge(_collector, value); } catch (Exception ex) { _error = ex; Dispose(); } } } public override void OnError(Exception error) { Dispose(); lock (_gate) { _error = error; } } public override void OnCompleted() { Dispose(); lock (_gate) { _hasCompleted = true; } } public override bool TryMoveNext([MaybeNullWhen(false)] out TResult current) { lock (_gate) { var error = _error; if (error != null) { current = default; _collector = default!; error.Throw(); } else { if (_hasCompleted) { if (_done) { current = default; _collector = default!; return false; } current = _collector; _done = true; } else { current = _collector; try { _collector = _getNewCollector(current); } catch { Dispose(); throw; } } } return true; } } } } }