From 85c6db7c423e34949870e3ea1a151bd5939e7adc Mon Sep 17 00:00:00 2001 From: Thomas Farr Date: Fri, 20 Sep 2024 15:38:49 +1200 Subject: [PATCH] Move enum parse impl Signed-off-by: Thomas Farr --- src/OpenSearch.Net/Api/Enums.cs | 38 ++---------- tests/Tests.YamlRunner/DoMapper.fs | 33 +++++++++- .../ClientConcepts/Enums/EnumParsingTests.cs | 62 ------------------- 3 files changed, 35 insertions(+), 98 deletions(-) delete mode 100644 tests/Tests/ClientConcepts/Enums/EnumParsingTests.cs diff --git a/src/OpenSearch.Net/Api/Enums.cs b/src/OpenSearch.Net/Api/Enums.cs index be5dbe344b..c551099799 100644 --- a/src/OpenSearch.Net/Api/Enums.cs +++ b/src/OpenSearch.Net/Api/Enums.cs @@ -55,7 +55,6 @@ namespace OpenSearch.Net public static partial class KnownEnums { private static readonly ConcurrentDictionary> EnumStringResolvers = new(); - private static readonly ConcurrentDictionary> EnumStringParsers = new(); static KnownEnums() => RegisterEnumStringResolvers(); @@ -77,44 +76,17 @@ private static Func GetEnumStringResolver(Type type) var dictionary = new Dictionary(values.Length); for (var index = 0; index < values.Length; index++) { - var value = values.GetValue(index); + var value = (Enum) values.GetValue(index); var info = type.GetField(value.ToString()); - var da = (EnumMemberAttribute[])info.GetCustomAttributes(typeof(EnumMemberAttribute), false); - var stringValue = da.Length > 0 ? da[0].Value : Enum.GetName(type, value); - dictionary.Add((Enum)value, stringValue); + var attr = info.GetCustomAttribute(false); + var stringValue = attr != null ? attr.Value : Enum.GetName(type, value); + dictionary.Add(value, stringValue); } - var isFlag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0; + var isFlag = type.GetCustomAttribute(false) != null; return isFlag ? e => string.Join(",", dictionary.Where(kv => e.HasFlag(kv.Key)).Select(kv => kv.Value)) : e => dictionary[e]; } - - internal static TEnum Parse(string value) - where TEnum : struct, Enum - { - var parser = EnumStringParsers.GetOrAdd(typeof(TEnum), GetEnumStringParser); - return (TEnum) parser(value); - } - - private static Func GetEnumStringParser(Type type) - { - var values = Enum.GetValues(type); - var dictionary = new Dictionary(values.Length); - for (var index = 0; index < values.Length; index++) - { - var value = values.GetValue(index); - var info = type.GetField(value.ToString()); - var da = (EnumMemberAttribute[])info.GetCustomAttributes(typeof(EnumMemberAttribute), false); - var stringValue = da.Length > 0 ? da[0].Value : Enum.GetName(type, value)!; - dictionary.Add(stringValue.ToLowerInvariant(), (Enum)value); - } - - var isFlag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0; - - return isFlag - ? s => (Enum)Enum.ToObject(type, s.ToLowerInvariant().Split(',').Aggregate(0, (acc, value) => acc | Convert.ToInt32(dictionary[value]))) - : s => dictionary[s.ToLowerInvariant()]; - } } } diff --git a/tests/Tests.YamlRunner/DoMapper.fs b/tests/Tests.YamlRunner/DoMapper.fs index 79d696d889..43873c73d9 100644 --- a/tests/Tests.YamlRunner/DoMapper.fs +++ b/tests/Tests.YamlRunner/DoMapper.fs @@ -34,6 +34,7 @@ open System.Collections.ObjectModel open System.Globalization open System.Linq open System.Linq.Expressions +open System.Runtime.Serialization open System.Threading.Tasks open Tests.YamlRunner.Models open OpenSearch.Net @@ -89,7 +90,7 @@ type FastApiInvoke(instance: Object, restName:string, pathParams:KeyedCollection let underlyingType = Nullable.GetUnderlyingType(t) if v = null then null else this.ArgConvert(v, underlyingType) - | t when t.IsEnum -> typeof.GetMethod("Parse").MakeGenericMethod(t).Invoke(null, [|v|]) + | t when t.IsEnum -> this.ArgEnum(this.ArgString v, t) | t -> failwithf $"unable to convert argument to type %s{t.FullName}" member this.ArgString (v:Object): string = @@ -101,7 +102,7 @@ type FastApiInvoke(instance: Object, restName:string, pathParams:KeyedCollection | :? double as i -> i.ToString(CultureInfo.InvariantCulture) | :? int64 as i -> i.ToString(CultureInfo.InvariantCulture) | :? Boolean as b -> if b then "false" else "true" - | e -> failwithf "unknown type %s " (e.GetType().Name) + | e -> failwithf $"unknown type %s{e.GetType().Name}" match v with | :? List as a -> @@ -111,7 +112,33 @@ type FastApiInvoke(instance: Object, restName:string, pathParams:KeyedCollection | [] -> "_all" | _ -> String.Join(',', values) | e -> toString e - + + member this.ArgEnum (v:String, t:Type): Enum = + let values = Enum.GetValues(t) + + let lookup = + values + |> Seq.cast + |> Seq.map (fun e -> + let field = t.GetField(e.ToString()) + let attr = field.GetCustomAttribute() + let stringValue = if attr <> null then attr.Value else Enum.GetName(t, e) + (stringValue.ToLowerInvariant(), e) + ) + |> Map.ofSeq + + let result = + v.ToLowerInvariant().Split(',') + |> Seq.map (_.Trim()) + |> Seq.map (fun s -> + match lookup.TryGetValue(s) with + | true, e -> e + | false, _ -> failwithf $"unable to find enum value %s{s}" + ) + |> Seq.map Convert.ToInt32 + |> Seq.reduce (fun acc e -> acc ||| e) + + Enum.ToObject(t, result) :?> Enum member this.CanInvoke (o:YamlMap) = let operationKeys = diff --git a/tests/Tests/ClientConcepts/Enums/EnumParsingTests.cs b/tests/Tests/ClientConcepts/Enums/EnumParsingTests.cs deleted file mode 100644 index be0e0487b4..0000000000 --- a/tests/Tests/ClientConcepts/Enums/EnumParsingTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 -* -* The OpenSearch Contributors require contributions made to -* this file be licensed under the Apache-2.0 license or a -* compatible open source license. -*/ - -using System; -using System.Runtime.Serialization; -using FluentAssertions; -using OpenSearch.Net; -using OpenSearch.OpenSearch.Xunit.XunitPlumbing; - -namespace Tests.ClientConcepts.Enums; - -public class EnumParsingTests -{ - [U] - public void CanParseRegularEnum() => KnownEnums.Parse("first").Should().Be(RegularEnum.First); - - [U] - public void CanParseEnumWithAttribute() => KnownEnums.Parse("second_value").Should().Be(EnumWithAttribute.Second); - - [U] - public void CanParseFlagsEnum() => KnownEnums.Parse("first,third").Should().Be(FlagsEnum.First | FlagsEnum.Third); - - [U] - public void CanParseFlagsEnumWithAttribute() => KnownEnums.Parse("first_value,third_value").Should().Be(FlagsEnumWithAttribute.First | FlagsEnumWithAttribute.Third); - -private enum RegularEnum - { - First, - Second - } - - private enum EnumWithAttribute - { - [EnumMember(Value = "first_value")] - First, - [EnumMember(Value = "second_value")] - Second - } - - [Flags] - private enum FlagsEnum - { - First = 1 << 0, - Second = 1 << 1, - Third = 1 << 2 - } - - [Flags] - private enum FlagsEnumWithAttribute - { - [EnumMember(Value = "first_value")] - First = 1 << 0, - [EnumMember(Value = "second_value")] - Second = 1 << 1, - [EnumMember(Value = "third_value")] - Third = 1 << 2 - } -}