Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the EnumerableInjector to allow entirely new items #30

Merged
merged 1 commit into from
Jul 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 89 additions & 6 deletions MonkeyLoader/Utility/EnumerableInjector.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace MonkeyLoader.Utility
{
Expand Down Expand Up @@ -33,35 +35,61 @@ public class EnumerableInjector<TOriginal, TTransformed> : IEnumerable<TTransfor

private Action _postfix = () => { };
private Action<TOriginal, TTransformed, bool> _postItem = (original, transformed, returned) => { };
private Action<TOriginal, TTransformed[], bool> _postItems;
private Action _prefix = () => { };
private Func<TOriginal, bool> _preItem = item => true;
private Func<TOriginal, TTransformed> _transformItem = item => throw new NotImplementedException("You're supposed to insert your own transformation function here!");
private Func<TOriginal, TTransformed> _transformItem = item => throw new NotImplementedException("You're supposed to insert your own transformation function here when not using TransformItems!");
private Func<TOriginal, IEnumerable<TTransformed>> _transformItems;

/// <summary>
/// Gets called when the wrapped enumeration returned the last item.
/// </summary>
public Action Postfix
{
get => _postfix;

[MemberNotNull(nameof(_postfix))]
set => _postfix = value ?? throw new ArgumentNullException(nameof(value), "Postfix can't be null!");
}

/// <summary>
/// Gets called for each item, with the transformed item, and whether it was passed through.
/// First thing to be called after execution returns to the enumerator after a yield return.
/// </summary>
/// <remarks>
/// Called by the default <see cref="TransformItems">TransformItems</see> for each item, but not directly.
/// </remarks>
public Action<TOriginal, TTransformed, bool> PostItem
{
get => _postItem;

[MemberNotNull(nameof(_postItem))]
set => _postItem = value ?? throw new ArgumentNullException(nameof(value), "PostItem can't be null!");
}

/// <summary>
/// Gets called for each item, with the transformed items, and whether they were passed through.
/// First thing to be called after execution returns to the enumerator after a yield return.
/// </summary>
/// <remarks>
/// Has precedence over <see cref="PostItem">PostItem</see> - the default implementation just passes the call through for each item.
/// </remarks>
public Action<TOriginal, TTransformed[], bool> PostItems
{
get => _postItems;

[MemberNotNull(nameof(_postItems))]
set => _postItems = value ?? throw new ArgumentNullException(nameof(value), "PostItems can't be null!");
}

/// <summary>
/// Gets called before the enumeration returns the first item.
/// </summary>
public Action Prefix
{
get => _prefix;

[MemberNotNull(nameof(_prefix))]
set => _prefix = value ?? throw new ArgumentNullException(nameof(value), "Prefix can't be null!");
}

Expand All @@ -71,18 +99,39 @@ public Action Prefix
public Func<TOriginal, bool> PreItem
{
get => _preItem;

[MemberNotNull(nameof(_preItem))]
set => _preItem = value ?? throw new ArgumentNullException(nameof(value), "PreItem can't be null!");
}

/// <summary>
/// Gets called for each item to transform it, even if it won't be passed through.
/// </summary>
/// <remarks>
/// Called by the default <see cref="TransformItems">TransformItems</see>, but not directly.
/// </remarks>
public Func<TOriginal, TTransformed> TransformItem
{
get => _transformItem;

[MemberNotNull(nameof(_transformItem))]
set => _transformItem = value ?? throw new ArgumentNullException(nameof(value), "TransformItem can't be null!");
}

/// <summary>
/// Gets called for each item to transform it into a sequence of items to return, even if it won't be passed through.
/// </summary>
/// <remarks>
/// Has precedence over <see cref="TransformItem">TransformItem</see> - the default implementation just passes the call through.
/// </remarks>
public Func<TOriginal, IEnumerable<TTransformed>> TransformItems
{
get => _transformItems;

[MemberNotNull(nameof(_transformItems))]
set => _transformItems = value ?? throw new ArgumentNullException(nameof(value), "TransformItems can't be null!");
}

/// <summary>
/// Creates a new instance of the <see cref="EnumerableInjector{TIn, TOut}"/> class using the supplied input <see cref="IEnumerable{T}"/> and transform function.
/// </summary>
Expand All @@ -95,11 +144,33 @@ public EnumerableInjector(IEnumerable<TOriginal> enumerable, Func<TOriginal, TTr
/// Creates a new instance of the <see cref="EnumerableInjector{TIn, TOut}"/> class using the supplied input <see cref="IEnumerator{T}"/> and transform function.
/// </summary>
/// <param name="enumerator">The enumerator to inject into and transform.</param>
/// <param name="transformItem">The transformation function.</param>
/// <param name="transformItem">The transformation function. Called through the default <see cref="TransformItems">TransformItems</see> implementation.</param>
public EnumerableInjector(IEnumerator<TOriginal> enumerator, Func<TOriginal, TTransformed> transformItem)
{
_enumerator = enumerator;
_enumerator = enumerator ?? throw new ArgumentNullException(nameof(enumerator));
TransformItem = transformItem;
TransformItems = DefaultTransformItems;
PostItems = DefaultPostItems;
}

/// <summary>
/// Creates a new instance of the <see cref="EnumerableInjector{TIn, TOut}"/> class using the supplied input <see cref="IEnumerable{T}"/> and transform function.
/// </summary>
/// <param name="enumerable">The enumerable to inject into and transform.</param>
/// <param name="transformItems">The transformation function. Takes precendence over <see cref="TransformItem">TransformItem</see>.</param>
public EnumerableInjector(IEnumerable<TOriginal> enumerable, Func<TOriginal, IEnumerable<TTransformed>> transformItems)
: this(enumerable.GetEnumerator(), transformItems) { }

/// <summary>
/// Creates a new instance of the <see cref="EnumerableInjector{TIn, TOut}"/> class using the supplied input <see cref="IEnumerator{T}"/> and transform function.
/// </summary>
/// <param name="enumerator">The enumerator to inject into and transform.</param>
/// <param name="transformItems">The transformation function. Takes precendence over <see cref="TransformItem">TransformItem</see>.</param>
public EnumerableInjector(IEnumerator<TOriginal> enumerator, Func<TOriginal, IEnumerable<TTransformed>> transformItems)
{
_enumerator = enumerator ?? throw new ArgumentNullException(nameof(enumerator));
TransformItems = transformItems;
PostItems = DefaultPostItems;
}

/// <summary>
Expand All @@ -114,12 +185,15 @@ public IEnumerator<TTransformed> GetEnumerator()
{
var item = _enumerator.Current;
var returnItem = PreItem(item);
var transformedItem = TransformItem(item);
var transformedItems = TransformItems(item).ToArray();

if (returnItem)
yield return transformedItem;
{
foreach (var transformedItem in transformedItems)
yield return transformedItem;
}

PostItem(item, transformedItem, returnItem);
PostItems(item, transformedItems, returnItem);
}

Postfix();
Expand All @@ -130,6 +204,15 @@ public IEnumerator<TTransformed> GetEnumerator()
/// </summary>
/// <returns>The injected and transformed enumeration without a generic type.</returns>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

private void DefaultPostItems(TOriginal original, TTransformed[] transformedItems, bool returned)
{
foreach (var transformedItem in transformedItems)
PostItem(original, transformedItem, returned);
}

private IEnumerable<TTransformed> DefaultTransformItems(TOriginal original)
=> TransformItem(original).Yield();
}

/// <summary>
Expand Down