| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="SmartEnumSource.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections; |
| | | 9 | | using System.Collections.Concurrent; |
| | | 10 | | using System.Collections.Generic; |
| | | 11 | | using System.Linq; |
| | | 12 | | using System.Reflection; |
| | | 13 | | |
| | | 14 | | #pragma warning disable IDE0130 // Namespace does not match folder structure |
| | | 15 | | namespace MyNet.Primitives; |
| | | 16 | | #pragma warning restore IDE0130 // Namespace does not match folder structure |
| | | 17 | | |
| | | 18 | | /// <summary> |
| | | 19 | | /// Discovers all instances declared on a smart enum type. |
| | | 20 | | /// </summary> |
| | | 21 | | public static class SmartEnumSource |
| | | 22 | | { |
| | 9 | 23 | | private static readonly ConcurrentDictionary<Type, IReadOnlyList<ISmartEnum>> Cache = new(); |
| | | 24 | | |
| | | 25 | | /// <summary> |
| | | 26 | | /// Returns all smart enum instances for the specified type. |
| | | 27 | | /// </summary> |
| | | 28 | | /// <param name="smartEnumType">The smart enum type.</param> |
| | | 29 | | /// <returns>All discovered smart enum instances.</returns> |
| | | 30 | | public static IReadOnlyList<ISmartEnum> GetAll(Type smartEnumType) |
| | | 31 | | { |
| | 30 | 32 | | ArgumentNullException.ThrowIfNull(smartEnumType); |
| | | 33 | | |
| | 27 | 34 | | return !typeof(ISmartEnum).IsAssignableFrom(smartEnumType) |
| | 27 | 35 | | ? throw new ArgumentException($"Type '{smartEnumType.FullName}' does not implement {nameof(ISmartEnum)}.", n |
| | 27 | 36 | | : Cache.GetOrAdd(smartEnumType, Discover); |
| | | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <summary> |
| | | 40 | | /// Returns all smart enum instances for the specified type. |
| | | 41 | | /// </summary> |
| | | 42 | | /// <typeparam name="TSmartEnum">The smart enum type.</typeparam> |
| | | 43 | | /// <returns>All discovered smart enum instances.</returns> |
| | | 44 | | public static IReadOnlyList<TSmartEnum> GetAll<TSmartEnum>() |
| | | 45 | | where TSmartEnum : class, ISmartEnum |
| | 9 | 46 | | => [.. GetAll(typeof(TSmartEnum)).Cast<TSmartEnum>()]; |
| | | 47 | | |
| | | 48 | | private static IReadOnlyList<ISmartEnum> Discover(Type smartEnumType) |
| | | 49 | | { |
| | 12 | 50 | | var smartEnumBase = FindSmartEnumBaseType(smartEnumType); |
| | 12 | 51 | | var allProperty = smartEnumBase?.GetProperty("All", BindingFlags.Public | BindingFlags.Static | BindingFlags.Dec |
| | | 52 | | |
| | 12 | 53 | | return allProperty?.GetValue(null) is IEnumerable values |
| | 12 | 54 | | ? [.. values.Cast<ISmartEnum>()] |
| | 12 | 55 | | : [.. smartEnumType |
| | 12 | 56 | | .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) |
| | 12 | 57 | | .Where(field => smartEnumType.IsAssignableFrom(field.FieldType)) |
| | 12 | 58 | | .Select(field => field.GetValue(null)) |
| | 12 | 59 | | .OfType<ISmartEnum>()]; |
| | | 60 | | } |
| | | 61 | | |
| | | 62 | | private static Type? FindSmartEnumBaseType(Type smartEnumType) |
| | | 63 | | { |
| | 54 | 64 | | for (var type = smartEnumType; type is not null; type = type.BaseType) |
| | | 65 | | { |
| | 24 | 66 | | if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(SmartEnum<,>)) |
| | | 67 | | { |
| | 9 | 68 | | return type; |
| | | 69 | | } |
| | | 70 | | } |
| | | 71 | | |
| | 3 | 72 | | return null; |
| | | 73 | | } |
| | | 74 | | } |
| | | 75 | | |