249 lines
6.7 KiB
C#
249 lines
6.7 KiB
C#
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace CertMgr.Core.Utils;
|
|
|
|
internal static class Extenders
|
|
{
|
|
private static readonly Dictionary<Type, string> _typeAliases = new Dictionary<Type, string>
|
|
{
|
|
{ typeof(long), "long" },
|
|
{ typeof(ulong), "ulong" },
|
|
{ typeof(int), "int" },
|
|
{ typeof(uint), "uint" },
|
|
{ typeof(short), "short" },
|
|
{ typeof(ushort), "ushort" },
|
|
{ typeof(byte), "byte" },
|
|
{ typeof(sbyte), "sbyte" },
|
|
{ typeof(double), "double" },
|
|
{ typeof(float), "float" },
|
|
{ typeof(decimal), "decimal" },
|
|
{ typeof(char), "char" },
|
|
{ typeof(string), "string" },
|
|
{ typeof(void), "void" },
|
|
{ typeof(bool), "bool" },
|
|
{ typeof(object), "object" },
|
|
};
|
|
|
|
public static string ToSeparatedList<T>(this IEnumerable<T> items, Func<T, string> formatter, string itemSeparator, string? lastItemSeparator = null)
|
|
{
|
|
using StringBuilderCache.ScopedBuilder lease = StringBuilderCache.AcquireScoped();
|
|
StringBuilder sb = lease.Builder;
|
|
|
|
ToSeparatedList(items, sb, formatter, itemSeparator, lastItemSeparator);
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static void ToSeparatedList<T>(this IEnumerable<T> items, StringBuilder sb, Func<T, string> formatter, string itemSeparator, string? lastItemSeparator = null)
|
|
{
|
|
if (!items.Any())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (formatter == null)
|
|
{
|
|
formatter = item => item?.ToString() ?? "<null>";
|
|
}
|
|
|
|
lastItemSeparator = lastItemSeparator ?? itemSeparator;
|
|
|
|
using (IEnumerator<T> enu = items.GetEnumerator())
|
|
{
|
|
if (enu.MoveNext())
|
|
{
|
|
sb.Append(formatter(enu.Current));
|
|
bool hasNext = enu.MoveNext();
|
|
while (hasNext)
|
|
{
|
|
T current = enu.Current;
|
|
hasNext = enu.MoveNext();
|
|
|
|
sb.Append(hasNext ? itemSeparator : lastItemSeparator);
|
|
sb.Append(formatter(current));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static string ToString(this Type type, bool includeNamespace)
|
|
{
|
|
if (type == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
using StringBuilderCache.ScopedBuilder lease = StringBuilderCache.AcquireScoped();
|
|
StringBuilder sb = lease.Builder;
|
|
type.AppendStringType(sb, includeNamespace);
|
|
string typeString = sb.ToString();
|
|
return typeString;
|
|
}
|
|
|
|
private static void AppendStringType(this Type type, StringBuilder sb, bool includeNamespace)
|
|
{
|
|
if (type == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (type.IsGenericType)
|
|
{
|
|
if (includeNamespace)
|
|
{
|
|
sb.Append(type.Namespace);
|
|
sb.Append('.');
|
|
}
|
|
sb.Append(type.Name.Substring(0, type.Name.IndexOf('`')));
|
|
sb.Append('<');
|
|
Type[] genericArguments = type.GenericTypeArguments;
|
|
for (int i = 0; i < genericArguments.Length; i++)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
sb.Append(',');
|
|
}
|
|
genericArguments[i].AppendStringType(sb, includeNamespace);
|
|
}
|
|
sb.Append('>');
|
|
}
|
|
else
|
|
{
|
|
if (_typeAliases.TryGetValue(type, out string? alias))
|
|
{
|
|
sb.Append(alias);
|
|
}
|
|
else
|
|
{
|
|
if (includeNamespace)
|
|
{
|
|
sb.Append(type.Namespace);
|
|
sb.Append('.');
|
|
}
|
|
sb.Append(type.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool Equivalent<T>(this IEnumerable<T?> left, IEnumerable<T?> right, IEqualityComparer<T?>? comparer = null) where T : notnull
|
|
{
|
|
if (ReferenceEquals(left, right))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (left == null || right == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (left.TryGetCount(out int lcount) && right.TryGetCount(out int rcount))
|
|
{
|
|
if (lcount != rcount)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int nullCount = 0;
|
|
Dictionary<T, int> cnt = new Dictionary<T, int>(comparer ?? EqualityComparer<T?>.Default);
|
|
foreach (T? item in left)
|
|
{
|
|
if (item == null)
|
|
{
|
|
nullCount++;
|
|
continue;
|
|
}
|
|
if (cnt.TryGetValue(item, out int value))
|
|
{
|
|
cnt[item] = ++value;
|
|
}
|
|
else
|
|
{
|
|
cnt.Add(item, 1);
|
|
}
|
|
}
|
|
|
|
foreach (T? item in right)
|
|
{
|
|
if (item == null)
|
|
{
|
|
nullCount--;
|
|
continue;
|
|
}
|
|
if (cnt.ContainsKey(item))
|
|
{
|
|
cnt[item]--;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool equals = nullCount == 0 && cnt.Values.All(c => c == 0);
|
|
return equals;
|
|
}
|
|
|
|
private static bool TryGetCount<T>(this IEnumerable<T> items, out int count)
|
|
{
|
|
count = -1;
|
|
|
|
bool success = false;
|
|
|
|
if (items is ICollection<T> colt)
|
|
{
|
|
count = colt.Count;
|
|
success = true;
|
|
}
|
|
else if (items is System.Collections.ICollection col)
|
|
{
|
|
count = col.Count;
|
|
success = true;
|
|
}
|
|
else if (items is IReadOnlyCollection<T> rocol)
|
|
{
|
|
count = rocol.Count;
|
|
success = true;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
public static bool IsAnyOf<T>(this T value, params T[] availableValues) where T : struct
|
|
{
|
|
if (!typeof(T).IsEnum)
|
|
{
|
|
throw new Exception(string.Format("Expected that the type is enum, got '{0}'", typeof(T).ToString(false)));
|
|
}
|
|
|
|
bool isOneOf = false;
|
|
|
|
Attribute? flags = typeof(T).GetCustomAttribute(typeof(FlagsAttribute));
|
|
if (flags == null)
|
|
{
|
|
if (availableValues != null && availableValues.Length > 0)
|
|
{
|
|
isOneOf = availableValues.Any(av => value.Equals(av));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ulong v = Convert.ToUInt64(value);
|
|
foreach (T availableValue in availableValues)
|
|
{
|
|
ulong av = Convert.ToUInt64(availableValue);
|
|
if ((v & av) == v)
|
|
{
|
|
isOneOf = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isOneOf;
|
|
}
|
|
|
|
}
|