From 28cca98cfe180693180160c5494d3cc07ea4ab61 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 4 Jul 2024 22:24:37 +0300 Subject: [PATCH] OnItemAdded behaves as a list if the rest implements it --- .../Linq/ObservableView.OnItemAdded.cs | 62 +++++++++++++++++-- .../StateMachines/ListProxyStateMachine.cs | 28 +++++++++ test/Kinetic.Tests/ObservableViewTests.cs | 1 + 3 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 src/Kinetic/Linq/StateMachines/ListProxyStateMachine.cs diff --git a/src/Kinetic/Linq/ObservableView.OnItemAdded.cs b/src/Kinetic/Linq/ObservableView.OnItemAdded.cs index acef16b..c086cce 100644 --- a/src/Kinetic/Linq/ObservableView.OnItemAdded.cs +++ b/src/Kinetic/Linq/ObservableView.OnItemAdded.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Kinetic.Linq.StateMachines; namespace Kinetic.Linq; @@ -6,12 +7,63 @@ namespace Kinetic.Linq; public static partial class ObservableView { public static ObserverBuilder> OnItemAdded(this ObserverBuilder> source, Action action) => - source.Do(change => - { - if (change.Action is ListChangeAction.Insert or ListChangeAction.Replace) - action(change.NewItem); - }); + source.ContinueWith, ListChange>(new() { Action = action }); public static ObserverBuilder> OnItemAdded(this ReadOnlyObservableList source, Action action) => source.Changed.ToBuilder().OnItemAdded(action); + + private struct OnItemAddedStateMachineFactory : IStateMachineFactory, ListChange> + { + public required Action Action { get; init; } + + public void Create(in TContinuation continuation, ObserverStateMachine> source) + where TContinuation : struct, IStateMachine> + { + source.ContinueWith>(new(continuation, Action)); + } + } + + private struct OnItemAddedStateMachine : IStateMachine> + where TContinuation : struct, IStateMachine> + { + private TContinuation _continuation; + private readonly Action _action; + + public OnItemAddedStateMachine(in TContinuation continuation, Action action) + { + _continuation = continuation; + _action = action; + } + + public StateMachineBox Box => + _continuation.Box; + + public StateMachine> Reference => + _continuation.Reference is IReadOnlyList list + ? new ListProxyStateMachine>(ref this, list) + : StateMachine>.Create(ref this); + + public StateMachine? Continuation => + _continuation.Reference; + + public void Initialize(StateMachineBox box) => + _continuation.Initialize(box); + + public void Dispose() => + _continuation.Dispose(); + + public void OnCompleted() => + _continuation.OnCompleted(); + + public void OnError(Exception error) => + _continuation.OnError(error); + + public void OnNext(ListChange value) + { + if (value.Action is ListChangeAction.Insert or ListChangeAction.Replace) + _action(value.NewItem); + + _continuation.OnNext(value); + } + } } \ No newline at end of file diff --git a/src/Kinetic/Linq/StateMachines/ListProxyStateMachine.cs b/src/Kinetic/Linq/StateMachines/ListProxyStateMachine.cs new file mode 100644 index 0000000..3de504e --- /dev/null +++ b/src/Kinetic/Linq/StateMachines/ListProxyStateMachine.cs @@ -0,0 +1,28 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Kinetic.Linq.StateMachines; + +internal sealed class ListProxyStateMachine : StateMachine, TStateMachine>, IReadOnlyList + where TStateMachine : struct, IStateMachine> +{ + private readonly IReadOnlyList _list; + + public ListProxyStateMachine(ref TStateMachine stateMachine, IReadOnlyList list) : + base(ref stateMachine) => _list = list; + + public ListProxyStateMachine(StateMachineReference, TStateMachine> stateMachine, IReadOnlyList list) : + base(stateMachine) => _list = list; + + public T this[int index] => + _list[index]; + + public int Count => + _list.Count; + + public IEnumerator GetEnumerator() => + _list.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + _list.GetEnumerator(); +} \ No newline at end of file diff --git a/test/Kinetic.Tests/ObservableViewTests.cs b/test/Kinetic.Tests/ObservableViewTests.cs index 25fcb71..331f2dd 100644 --- a/test/Kinetic.Tests/ObservableViewTests.cs +++ b/test/Kinetic.Tests/ObservableViewTests.cs @@ -385,6 +385,7 @@ public void OnItemRemoved() .Subscribe(); list.Add(0); + list.RemoveAt(0); Assert.True(handledBefore); Assert.True(handledAfter);