using System.Collections.Concurrent;
using System.Reflection;
using RestEase.Implementation;
using System.Linq;
using RestEase.Platform;
#if !NETSTANDARD1_1
using System.ComponentModel;
using System.Runtime.Serialization;
#endif
namespace RestEase
{
///
///
/// A serializer that handles enum values specially, serializing them into their display value
/// as defined by a EnumMember, DisplayName, or Display attribute (in that order).
///
public class StringEnumRequestPathParamSerializer : RequestPathParamSerializer
{
private static readonly ConcurrentDictionary cache = new ConcurrentDictionary();
///
///
/// Serialize a path parameter whose value is scalar (not a collection), into a string value
///
/// Type of the value to serialize
/// Value of the path parameter
/// Extra info which may be useful to the serializer
/// A string value to use as path parameter
///
/// If the value is an enum value, the serializer will check if it has an EnumMember, DisplayName or Display
/// attribute, and if so return the value of that instead (in that order of preference).
///
public override string? SerializePathParam(T value, RequestPathParamSerializerInfo info)
{
if (value == null)
{
return null;
}
var typeInfo = typeof(T).GetTypeInfo();
if (!typeInfo.IsEnum)
{
return ToStringHelper.ToString(value, info.Format, info.FormatProvider);
}
if (cache.TryGetValue(value, out string? stringValue))
{
return stringValue;
}
stringValue = value.ToString();
var fieldInfo = typeInfo.GetField(stringValue);
if (fieldInfo == null)
{
return CacheAdd(value, stringValue);
}
#if !NETSTANDARD1_1
var enumMemberAttribute = fieldInfo.GetCustomAttribute();
if (enumMemberAttribute != null)
{
return CacheAdd(value, enumMemberAttribute.Value);
}
var displayNameAttribute = fieldInfo.GetCustomAttribute();
if (displayNameAttribute != null)
{
return CacheAdd(value, displayNameAttribute.DisplayName);
}
#endif
// netstandard can get this by referencing System.ComponentModel.DataAnnotations, (and framework
// can get this by referencing the assembly). However we don't want a dependency on this nuget package,
// for something so niche, so do a reflection-only load
var displayAttribute = fieldInfo.CustomAttributes
.FirstOrDefault(x => x.AttributeType.FullName == "System.ComponentModel.DataAnnotations.DisplayAttribute");
if (displayAttribute != null)
{
object? name = displayAttribute.NamedArguments.FirstOrDefault(x => x.MemberName == "Name").TypedValue.Value;
if (name != null)
{
return CacheAdd(value, (string)name);
}
}
return CacheAdd(value, stringValue);
}
private static string CacheAdd(object key, string value)
{
cache.TryAdd(key, value);
return value;
}
}
}