// Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; namespace QtVsTools.Common { /// /// Extended enum support: /// * Customized cast of enum values to arbitrary types /// public static class EnumExt { static LazyFactory StaticLazy { get; } = new(); /// /// Wrapper for enum cast values. /// /// Type of cast output /// /// Cast attributes associated with enum values must implement this interface. /// public interface ICast { T Value { get; } } /// /// String cast attribute associated to an enum value. /// /// /// enum Foobar { /// Foo, /// [EnumExt.String("Bahr")] Bar /// } /// [AttributeUsage(AttributeTargets.All)] public sealed class StringAttribute : Attribute, ICast { public string Value { get; } public StringAttribute(string str) { Value = str; } } /// /// Cast enum value to type T. /// /// Cast output type. /// Input enum value. /// /// Value of type T associated to the enum value by an Attribute implementing /// ICast. If no attribute is found, returns a default value. /// /// /// enum Foobar /// { /// Foo, /// [EnumExt.String("Bahr")] Bar /// } /// Foobar foo = Foobar.Foo; /// Foobar bar = Foobar.Bar; /// string fooCastString = foo.Cast(); // "Foo" /// string barCastString = bar.Cast(); // "Bahr" /// string fooToString = foo.ToString(); // "Foo" /// string barToString = bar.ToString(); // "Bar" /// public static T Cast(this Enum value) { if (FindCastAttrib(value) is { } cast) return cast.Value; return Default(value); } /// /// Compare enum value with instance/value of type T. /// /// Cast/comparison type. /// Instance/value of type T to compare with. /// Enum value to compare with. /// true if cast of valueEnum is equal to valueT, false otherwise public static bool EqualTo(this T valueT, Enum valueEnum) { return valueT.Equals(valueEnum.Cast()); } /// /// Convert type T to enum /// public static bool TryCast(this T valueT, out TEnum value) where TEnum : struct { value = default; IEnumerable enumValues = Enum.GetValues(typeof(TEnum)).OfType() .Where(valueEnum => valueEnum.Cast().Equals(valueT)) .ToList(); if (enumValues.Any()) value = (TEnum)Enum.ToObject(typeof(TEnum), enumValues.FirstOrDefault()); return enumValues.Any(); } /// /// Convert type T to enum /// public static TEnum Cast(this T valueT, TEnum defaultValue) where TEnum : struct { return TryCast(valueT, out TEnum value) ? value : defaultValue; } /// /// Get list of values of enum type /// public static IEnumerable GetValues() where TEnum : struct { Debug.Assert(typeof(TEnum).IsEnum); return Enum.GetValues(typeof(TEnum)).OfType(); } /// /// Get list of values of enum type converted to type T /// public static IEnumerable GetValues(Type enumType) { return Enum.GetValues(enumType).OfType() .Select(value => value.Cast()); } /// /// Default cast of enum value to type T. /// /// Cast output type. /// Input enum value. /// /// Default value of type T associated with the enum value: /// * if T is string: returns the enum value name as string; /// * if T is an integer type: returns the underlying enum integer value; /// * otherwise: default value for type T (e.g. null for reference types). /// static T Default(Enum value) { Type enumType = value.GetType(); Type baseType = Enum.GetUnderlyingType(enumType); Type outputType = typeof(T); if (outputType.IsAssignableFrom(enumType) || outputType.IsAssignableFrom(baseType)) return (T)(object)value; if (outputType == typeof(string)) return (T)(object)Enum.GetName(value.GetType(), value); return default; } /// /// Find cast attribute. /// /// Cast output type. /// Input enum value. /// /// First cast attribute of type T found associated with the enum value, or null in case a /// suitable attribute is not found. /// static ICast FindCastAttrib(Enum value) { Type enumType = value.GetType(); string valueName = Enum.GetName(enumType, value); if (string.IsNullOrEmpty(valueName)) return null; FieldInfo enumField = enumType.GetField(valueName); if (enumField == null) return null; return CastAttribTypes .Where(type => typeof(ICast).IsAssignableFrom(type)) .Select(type => Attribute.GetCustomAttribute(enumField, type) as ICast) .FirstOrDefault(); } /// /// List of cast attribute types. /// /// /// Future cast attribute types need to be added to this list. /// static IEnumerable CastAttribTypes => StaticLazy.Get(() => CastAttribTypes, () => new[] { typeof(StringAttribute) }); } }