PowerShellUtils/Lib/Extensions/Extensions.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Text;
 
namespace PowerShellStandardModule1.Lib.Extensions;
 
public static class Extensions
{
    public static void EnqueueRange<T>(this Queue<T> queue, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            queue.Enqueue(item);
        }
    }
 
    public static void PushRange<T>(this Stack<T> stack, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            stack.Push(item);
        }
    }
 
    public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
    {
        foreach (var item in items)
        {
            action(item);
        }
    }
 
    public static void ForEach<T, TReturn>(this IEnumerable<T> items, Func<T, TReturn> action)
    {
        foreach (var item in items)
        {
            action(item);
        }
    }
 
    public static Stack<T> ToStack<T>(this IEnumerable<T> items) => new(items);
 
    public static bool NotEmpty<T>(this Stack<T> stack) => stack.Count != 0;
 
    public static TReturn Thru<T, TReturn>(this T src, Func<T, TReturn> fn) => fn(src);
 
    public static T Tap<T>(this T src, Action<T> fn)
    {
        fn(src);
        return src;
    }
 
    public static T Tap<T, TReturn>(this T src, Func<T, TReturn> fn)
    {
        fn(src);
        return src;
    }
 
    public static (IList<T> True, IList<T> False) PartitionBy<T>(this IEnumerable<T> source, Predicate<T> predicate)
    {
        var left = new List<T>();
        var right = new List<T>();
 
        foreach (var item in source)
        {
            if (predicate(item))
            {
                left.Add(item);
            }
            else
            {
                right.Add(item);
            }
        }
 
        return (left, right);
    }
 
    public static Func<T, TReturn> AndThen<T, TIntermediate, TReturn>(
        this Func<T, TIntermediate> fn1,
        Func<TIntermediate, TReturn> fn2
    ) =>
        x => fn2(fn1(x));
 
    public static Func<T, TReturn> Compose<T, TIntermediate, TReturn>(
        this Func<TIntermediate, TReturn> fn1,
        Func<T, TIntermediate> fn2
    ) =>
        x => fn1(fn2(x));
 
 
    public static string StringJoin<T>(this IEnumerable<T> items, string separator = "") =>
        string.Join(separator, items);
 
    public static StringBuilder ToStringBuilder(this IEnumerable<string> strings) =>
        strings.Aggregate(new StringBuilder(), (sb, x) => sb.AppendLine(x));
 
    public static void SetFieldValue<T>(this T src, string key, object? value) where T : notnull
    {
        var prop = src
               .GetType()
               .GetField(key) ??
            throw new ArgumentException($"Field {key} not found on {src.GetType().Name}");
        prop.SetValue(src, value);
    }
 
    public static object? GetFieldValue<T>(this T src, string key) where T : notnull
    {
        var prop = src
               .GetType()
               .GetField(key) ??
            throw new ArgumentException($"Field {key} not found on {src.GetType().Name}");
        return prop.GetValue(src);
    }
 
    public static void SetPropertyValue<T>(this T src, string key, object? value) where T : notnull
    {
        var prop = src
               .GetType()
               .GetProperty(key) ??
            throw new ArgumentException($"Property {key} not found on {src.GetType().Name}");
        prop.SetValue(src, value);
    }
 
    public static object? GetPropertyValue<T>(this T src, string key) where T : notnull
    {
        var prop = src
               .GetType()
               .GetProperty(key) ??
            throw new ArgumentException($"Property {key} not found on {src.GetType().Name}");
        return prop.GetValue(src);
    }
 
    public static IEnumerable<KeyValuePair<TKey, TReturn>> SelectValues<TKey, TValue, TReturn>(
        this IEnumerable<KeyValuePair<TKey, TValue>> source,
        Func<TValue, TReturn> fn
    ) =>
        source.Select(x => new KeyValuePair<TKey, TReturn>(x.Key, fn(x.Value)));
 
 
    public static IEnumerable<KeyValuePair<TKey, TReturn>> SelectValues<TKey, TValue, TReturn>(
        this IEnumerable<KeyValuePair<TKey, TValue>> source,
        Func<TValue, TKey, TReturn> fn
    ) =>
        source.Select(x => new KeyValuePair<TKey, TReturn>(x.Key, fn(x.Value, x.Key)));
 
 
    public static IEnumerable<KeyValuePair<T, TReturn>> SelectManyToPairs<T, TReturn>(
        this IEnumerable<T> source,
        Func<T, IEnumerable<TReturn>> fn
    )
    {
        return from item in source
            from innerItem in fn(item)
            select KeyValuePair.Create(item, innerItem);
    }
 
 
    public static IEnumerable<KeyValuePair<TKey, TReturn>> ChooseEntries<TKey, TValue, TReturn>(
        this IEnumerable<KeyValuePair<TKey, TValue>> source,
        Func<TValue, IEnumerable<TReturn>> fn
    ) =>
        source.SelectMany(
            x => fn(x.Value)
               .Select(y => new KeyValuePair<TKey, TReturn>(x.Key, y))
        );
 
 
    public static IEnumerable<KeyValuePair<TKey, TReturn>> ToPairs<TKey, TReturn>(
        this IEnumerable<TKey> source,
        Func<TKey, TReturn> fn
    ) =>
        source.Select(x => new KeyValuePair<TKey, TReturn>(x, fn(x)));
 
    public static IEnumerable<string> GetFieldKeys<T>(this T src) where T : notnull =>
        src
           .GetType()
           .GetFields()
           .Select(x => x.Name);
 
    public static IEnumerable<string> GetPropertyKeys<T>(this T src) where T : notnull =>
        src
           .GetType()
           .GetProperties()
           .Select(x => x.Name);
 
    public static IEnumerable<Action> ToActions<T>(this IEnumerable<T> items, Action<T> action) =>
        items.Select(x => new Action(() => action(x)));
 
    public static IEnumerable<KeyValuePair<string, object?>> ToPairsFromFields(this object obj)
    {
        return obj
           .GetFieldKeys()
           .ToPairs(obj.GetFieldValue);
    }
 
    public static IEnumerable<KeyValuePair<string, object?>> ToPairsFromProperties(this object obj)
    {
        return obj
           .GetPropertyKeys()
           .ToPairs(obj.GetPropertyValue);
    }
 
    public static IEnumerable<KeyValuePair<string, T>> ToPairsFromProperties<T>(this object obj)
    {
        foreach (var item in obj.ToPairsFromProperties())
        {
            if (item.Value is T val)
            {
                yield return item.WithValue(val);
            }
        }
    }
 
 
    public static IEnumerable<KeyValuePair<TKey, TValue>>?
        CastKeyValuePairs<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, object>> source)
    {
        return source as IEnumerable<KeyValuePair<TKey, TValue>>;
    }
 
    public static IEnumerable<TKey> Keys<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source) =>
        source.Select(x => x.Key);
 
    public static IEnumerable<TValue> Values<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source) =>
        source.Select(x => x.Value);
 
    public static IEnumerable<KeyValuePair<TKey, TResult>> JoinToKeyValuePairs<TOuter, TInner, TKey, TResult>(
        this IEnumerable<TOuter> outerItems,
        IEnumerable<TInner> innerItems,
        Func<TOuter, TKey> outerKeySelector,
        Func<TInner, TKey> innerKeySelector,
        Func<TOuter, TInner, TResult> resultSelector
    ) where TKey : notnull
    {
        var outer = outerItems.ToImmutableDictionary(outerKeySelector);
        var inner = innerItems.ToImmutableDictionary(innerKeySelector);
        var commonKeys = outer.Keys.Intersect(inner.Keys);
 
        return commonKeys.Select(
            key =>
            {
                var res = resultSelector(outer[key], inner[key]);
                var kvp = KeyValuePair.Create(key, res);
                return kvp;
            }
        );
    }
 
    public static IEnumerable<KeyValuePair<TKey, (TOuter, TInner)>> Join<TOuter, TInner, TKey>(
        this IEnumerable<TOuter> outerItems,
        IEnumerable<TInner> innerItems,
        Func<TOuter, TKey> outerKeySelector,
        Func<TInner, TKey> innerKeySelector
    ) where TKey : notnull
    {
        return outerItems.JoinToKeyValuePairs(
            innerItems, outerKeySelector, innerKeySelector,
            (x, y) => (x, y)
        );
    }
 
    public static IEnumerable<KeyValuePair<TKey, (TValue, TValue2)>> Join<TKey, TValue, TValue2>(
        this IEnumerable<KeyValuePair<TKey, TValue>> outerItems,
        IEnumerable<KeyValuePair<TKey, TValue2>> innerItems
    ) where TKey : notnull
    {
        var outer = outerItems.ToImmutableDictionary();
        var inner = innerItems.ToImmutableDictionary();
        var commonKeys = outer.Keys.Intersect(inner.Keys);
 
        return commonKeys.Select(
            key =>
            {
                var res = (outer[key], inner[key]);
                var kvp = KeyValuePair.Create(key, res);
                return kvp;
            }
        );
    }
 
 
    public static IEnumerable<KeyValuePair<string, T>> JoinObject<T>(
        this object obj1,
        object obj2,
        Func<object?, object?, T> joiner
    )
    {
        var keys1 = obj1
           .GetFieldKeys()
           .ToHashSet();
        var keys2 = obj2
           .GetFieldKeys()
           .ToHashSet();
 
        var commonKeys = keys1.Intersect(keys2);
 
        foreach (var key in commonKeys)
        {
            var val1 = obj1.GetFieldValue(key);
            var val2 = obj2.GetFieldValue(key);
 
            var res = joiner(val1, val2);
            var kvp = new KeyValuePair<string, T>(key, res);
            yield return kvp;
        }
    }
 
    public static KeyValuePair<TKey, TResult> WithValue<TKey, TValue, TResult>(
        this KeyValuePair<TKey, TValue> source,
        TResult value
    )
    {
        return KeyValuePair.Create(source.Key, value);
    }
 
    public static T[] InArray<T>(this T item) => [item];
 
    public static IEnumerable<(T Item1, TReturn Item2)> ToTuples<T, TReturn>(
        this IEnumerable<T> items,
        Func<T, TReturn> fn
    ) =>
        items.Select(x => (x, fn(x)));
 
 
    public static IEnumerable<(T Item1, TReturn Item2)> SelectManyToTuples<T, TReturn>(
        this IEnumerable<T> items,
        Func<T, IEnumerable<TReturn>> fn
    )
    {
        return from item in items
            from innerItem in fn(item)
            select (item, innerItem);
    }
 
 
    public static IEnumerable<KeyValuePair<PropertyInfo, T>> GetPropertiesWithAttribute<T>(this object obj)
        where T : Attribute
    {
        return obj
           .GetType()
           .GetProperties()
           .SelectManyToPairs(
                x => x.GetCustomAttribute<T>(false) is { } attr
                    ? attr.InArray()
                    : []
            );
    }
 
 
    public static IEnumerable<(T, T)> Sliding<T>(this IEnumerable<T> src)
    {
        using var iterator = src.GetEnumerator();
 
 
        if (!iterator.MoveNext()) yield break;
 
        var prev = iterator.Current;
 
        while (iterator.MoveNext())
        {
            yield return (prev, iterator.Current);
            prev = iterator.Current;
        }
    }
 
    public static bool IsEmpty<T>(this IList<T> source) => source.Count == 0;
 
    public static bool NotEmpty<T>(this Queue<T> source) => source.Count != 0;
 
    public static void MergeInto(this object source, object target)
    {
        foreach (var (key, value) in source.ToPairsFromProperties())
        {
            target.SetPropertyValue(key, value);
        }
    }
 
    public static IEnumerable<T> TapEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
            yield return item;
        }
    }
 
    public static Func<T, TReturn> CopySignature<T, TReturn>(this Func<T, TReturn> _, Func<T, TReturn> func) => func;
 
    public static Func<T, bool> Invert<T>(this Func<T, bool> fn) => x => !fn(x);
 
    public static Func<T, bool> AggregateAny<T>(this IEnumerable<Func<T, bool>> fns) => x => fns.Any(fn => fn(x));
 
    public static Func<T, bool> AggregateAll<T>(this IEnumerable<Func<T, bool>> fns) => x => fns.All(fn => fn(x));
 
    public static IEnumerable<PropertyInfo> GetPropertiesOfType<T>(this Type type)
    {
        var properties = type.GetProperties();
        foreach (var property in properties)
        {
            if (property.PropertyType == typeof(T) && property.CanWrite)
            {
                yield return property;
            }
        }
    }
 
    public static IEnumerable<PropertyInfo> GetPropertiesOfType<T>(this object obj)
    {
        var type = obj.GetType();
        return type.GetPropertiesOfType<T>();
    }
 
    public static IEnumerable GetPropertyValues(this object obj)
    {
        var type = obj.GetType();
        var properties = type.GetProperties();
        foreach (var property in properties)
        {
            yield return property.GetValue(obj);
        }
    }
 
    public static int MinZero(this int value) => Math.Max(0, value);
   
}
 
 
public static class Queue
{
    public static Queue<T> StartWith<T>(T item) => new(item.InArray());
}
 
public static class Stack
{
    public static Stack<T> From<T>(IEnumerable<T> items) => new(items);
}