Skip to content

Commit

Permalink
Merge pull request #154 from Cysharp/append-prepend-improv
Browse files Browse the repository at this point in the history
Add Append/Prepend(IEnumerable<T>), (Func<T>), (TState, Func<TState, T>)
  • Loading branch information
neuecc authored Mar 2, 2024
2 parents b72a8d8 + 4f59ae1 commit b484456
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 1 deletion.
179 changes: 178 additions & 1 deletion src/R3/Operators/AppendPrepend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,41 @@ public static Observable<T> Append<T>(this Observable<T> source, T value)
return new AppendPrepend<T>(source, value, append: true);
}

public static Observable<T> Append<T>(this Observable<T> source, IEnumerable<T> values)
{
return new AppendPrependEnumerable<T>(source, values, append: true);
}

public static Observable<T> Append<T>(this Observable<T> source, Func<T> valueFactory)
{
return new AppendPrependFactory<T>(source, valueFactory, append: true);
}

public static Observable<T> Append<T, TState>(this Observable<T> source, TState state, Func<TState, T> valueFactory)
{
return new AppendPrependFactory<T, TState>(source, state, valueFactory, append: true);
}

public static Observable<T> Prepend<T>(this Observable<T> source, T value)
{
return new AppendPrepend<T>(source, value, append: false);
}
}

public static Observable<T> Prepend<T>(this Observable<T> source, IEnumerable<T> values)
{
return new AppendPrependEnumerable<T>(source, values, append: false);
}

public static Observable<T> Prepend<T>(this Observable<T> source, Func<T> valueFactory)
{
return new AppendPrependFactory<T>(source, valueFactory, append: false);
}

public static Observable<T> Prepend<T, TState>(this Observable<T> source, TState state, Func<TState, T> valueFactory)
{
return new AppendPrependFactory<T, TState>(source, state, valueFactory, append: false);
}
}

internal sealed class AppendPrepend<T>(Observable<T> source, T value, bool append) : Observable<T>
{
Expand Down Expand Up @@ -53,3 +82,151 @@ protected override void OnCompletedCore(Result result)
}
}
}

internal sealed class AppendPrependEnumerable<T>(Observable<T> source, IEnumerable<T> values, bool append) : Observable<T>
{
protected override IDisposable SubscribeCore(Observer<T> observer)
{
if (!append) // prepend
{
if (values is T[] array)
{
foreach (var value in array)
{
observer.OnNext(value);
}
}
else
{
foreach (var value in values)
{
observer.OnNext(value);
}
}

return source.Subscribe(observer.Wrap());
}

return source.Subscribe(new _Append(observer, values));
}

sealed class _Append(Observer<T> observer, IEnumerable<T> values) : Observer<T>
{
protected override void OnNextCore(T value)
{
observer.OnNext(value);
}

protected override void OnErrorResumeCore(Exception error)
{
observer.OnErrorResume(error);
}

protected override void OnCompletedCore(Result result)
{
if (result.IsFailure)
{
observer.OnCompleted(result);
}
else
{
if (values is T[] array)
{
foreach (var value in array)
{
observer.OnNext(value);
}
}
else
{
foreach (var value in values)
{
observer.OnNext(value);
}
}

observer.OnCompleted();
}
}
}
}

internal sealed class AppendPrependFactory<T>(Observable<T> source, Func<T> valueFactory, bool append) : Observable<T>
{
protected override IDisposable SubscribeCore(Observer<T> observer)
{
if (!append) // prepend
{
observer.OnNext(valueFactory());
return source.Subscribe(observer.Wrap());
}

return source.Subscribe(new _Append(observer, valueFactory));
}

sealed class _Append(Observer<T> observer, Func<T> valueFactory) : Observer<T>
{
protected override void OnNextCore(T value)
{
observer.OnNext(value);
}

protected override void OnErrorResumeCore(Exception error)
{
observer.OnErrorResume(error);
}

protected override void OnCompletedCore(Result result)
{
if (result.IsFailure)
{
observer.OnCompleted(result);
}
else
{
observer.OnNext(valueFactory());
observer.OnCompleted();
}
}
}
}

internal sealed class AppendPrependFactory<T, TState>(Observable<T> source, TState state, Func<TState, T> valueFactory, bool append) : Observable<T>
{
protected override IDisposable SubscribeCore(Observer<T> observer)
{
if (!append) // prepend
{
observer.OnNext(valueFactory(state));
return source.Subscribe(observer.Wrap());
}

return source.Subscribe(new _Append(observer, state, valueFactory));
}

sealed class _Append(Observer<T> observer, TState state, Func<TState, T> valueFactory) : Observer<T>
{
protected override void OnNextCore(T value)
{
observer.OnNext(value);
}

protected override void OnErrorResumeCore(Exception error)
{
observer.OnErrorResume(error);
}

protected override void OnCompletedCore(Result result)
{
if (result.IsFailure)
{
observer.OnCompleted(result);
}
else
{
observer.OnNext(valueFactory(state));
observer.OnCompleted();
}
}
}
}
68 changes: 68 additions & 0 deletions tests/R3.Tests/OperatorTests/ConcatAppendPrependTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,72 @@ public void ConcatNestedSources_Empty()
list.AssertIsCompleted();
list.AssertEqual([]);
}

// Prepend factory
[Fact]
public void PrependFactory()
{
{
using var list = Observable.Range(1, 3).Prepend(() => 10).ToLiveList();
list.AssertEqual([10, 1, 2, 3]);
}
// with state
{
var o = new { V = 20 };
using var list = Observable.Range(1, 3).Prepend(o, static x => x.V).ToLiveList();
list.AssertEqual([20, 1, 2, 3]);
}
}

[Fact]
public void PrependEnumerable()
{
// Array
{
using var list = Observable.Range(1, 3).Prepend([10, 11, 12]).ToLiveList();
list.AssertEqual([10, 11, 12, 1, 2, 3]);
}
// Pure Enumerable
{
using var list = Observable.Range(1, 3).Prepend(Iterate()).ToLiveList();
list.AssertEqual([100, 200, 300, 1, 2, 3]);
}
}

[Fact]
public void AppendFactory()
{
{
using var list = Observable.Range(1, 3).Append(() => 10).ToLiveList();
list.AssertEqual([1, 2, 3, 10]);
}
// with state
{
var o = new { V = 20 };
using var list = Observable.Range(1, 3).Append(o, static x => x.V).ToLiveList();
list.AssertEqual([1, 2, 3, 20]);
}
}

[Fact]
public void AppendEnumerable()
{
// Array
{
using var list = Observable.Range(1, 3).Append([10, 11, 12]).ToLiveList();
list.AssertEqual([1, 2, 3, 10, 11, 12]);
}
// Pure Enumerable
{
using var list = Observable.Range(1, 3).Append(Iterate()).ToLiveList();
list.AssertEqual([1, 2, 3, 100, 200, 300]);
}
}

static IEnumerable<int> Iterate()
{
yield return 100;
yield return 200;
yield return 300;
}
}

0 comments on commit b484456

Please sign in to comment.