using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Nancy; using NodaTime; using NodaTime.Text; using Sharpility.Base; using Sharpility.Extensions; using Sharpility.Util; namespace {{packageName}}.{{packageContext}}.Utils { internal static class Parameters { private static readonly IDictionary> Parsers = CreateParsers(); internal static TValue ValueOf(dynamic parameters, Request request, string name, ParameterType parameterType) { var valueType = typeof(TValue); var valueUnderlyingType = Nullable.GetUnderlyingType(valueType); var isNullable = default(TValue) == null; string value = RawValueOf(parameters, request, name, parameterType); Preconditions.Evaluate(!string.IsNullOrEmpty(value) || isNullable, string.Format("Required parameter: '{0}' is missing", name)); if (value == null && isNullable) { return default(TValue); } if (valueType.IsEnum || (valueUnderlyingType != null && valueUnderlyingType.IsEnum)) { return EnumValueOf(name, value); } return ValueOf(parameters, name, value, valueType, request, parameterType); } private static string RawValueOf(dynamic parameters, Request request, string name, ParameterType parameterType) { try { switch (parameterType) { case ParameterType.Query: string querValue = request.Query[name]; return querValue; case ParameterType.Path: string pathValue = parameters[name]; return pathValue; case ParameterType.Header: var headerValue = request.Headers[name]; return headerValue != null ? string.Join(",", headerValue) : null; } } catch (Exception e) { throw new InvalidOperationException(string.Format("Could not obtain value of '{0}' parameter", name), e); } throw new InvalidOperationException(string.Format("Parameter with type: {0} is not supported", parameterType)); } private static TValue EnumValueOf(string name, string value) { var valueType = typeof(TValue); var enumType = valueType.IsEnum ? valueType : Nullable.GetUnderlyingType(valueType); Preconditions.IsNotNull(enumType, () => new InvalidOperationException( string.Format("Could not parse parameter: '{0}' to enum. Type {1} is not enum", name, valueType))); var values = Enum.GetValues(enumType); foreach (var entry in values) { if (entry.ToString().EqualsIgnoreCases(value) || ((int)entry).ToString().EqualsIgnoreCases(value)) { return (TValue)entry; } } throw new ArgumentException(string.Format("Parameter: '{0}' value: '{1}' is not supported. Expected one of: {2}", name, value, Strings.ToString(values))); } private static TValue ValueOf(dynamic parameters, string name, string value, Type valueType, Request request, ParameterType parameterType) { var parser = Parsers.GetIfPresent(valueType); if (parser != null) { return ParseValueUsing(name, value, valueType, parser); } if (parameterType == ParameterType.Path) { return DynamicValueOf(parameters, name); } if (parameterType == ParameterType.Query) { return DynamicValueOf(request.Query, name); } throw new InvalidOperationException(string.Format("Could not get value for {0} with type {1}", name, valueType)); } private static TValue ParseValueUsing(string name, string value, Type valueType, Func parser) { var result = parser(Parameter.Of(name, value)); try { return (TValue)result; } catch (InvalidCastException) { throw new InvalidOperationException( string.Format("Could not parse parameter: '{0}' with value: '{1}'. " + "Received: '{2}', expected: '{3}'.", name, value, result.GetType(), valueType)); } } private static TValue DynamicValueOf(dynamic parameters, string name) { string value = parameters[name]; try { TValue result = parameters[name]; return result; } catch (InvalidCastException) { throw new InvalidOperationException(Strings.Format("Parameter: '{0}' value: '{1}' could not be parsed. " + "Expected type: '{2}' is not supported", name, value, typeof(TValue))); } catch (Exception e) { throw new InvalidOperationException(string.Format("Could not get '{0}' value of '{1}' type dynamicly", name, typeof(TValue)), e); } } private static IDictionary> CreateParsers() { var parsers = ImmutableDictionary.CreateBuilder>(); parsers.Put(typeof(string), value => value.Value); parsers.Put(typeof(bool), SafeParse(bool.Parse)); parsers.Put(typeof(bool?), SafeParse(bool.Parse)); parsers.Put(typeof(byte), SafeParse(byte.Parse)); parsers.Put(typeof(sbyte?), SafeParse(sbyte.Parse)); parsers.Put(typeof(short), SafeParse(short.Parse)); parsers.Put(typeof(short?), SafeParse(short.Parse)); parsers.Put(typeof(ushort), SafeParse(ushort.Parse)); parsers.Put(typeof(ushort?), SafeParse(ushort.Parse)); parsers.Put(typeof(int), SafeParse(int.Parse)); parsers.Put(typeof(int?), SafeParse(int.Parse)); parsers.Put(typeof(uint), SafeParse(uint.Parse)); parsers.Put(typeof(uint?), SafeParse(uint.Parse)); parsers.Put(typeof(long), SafeParse(long.Parse)); parsers.Put(typeof(long?), SafeParse(long.Parse)); parsers.Put(typeof(ulong), SafeParse(ulong.Parse)); parsers.Put(typeof(ulong?), SafeParse(ulong.Parse)); parsers.Put(typeof(float), SafeParse(float.Parse)); parsers.Put(typeof(float?), SafeParse(float.Parse)); parsers.Put(typeof(double), SafeParse(double.Parse)); parsers.Put(typeof(double?), SafeParse(double.Parse)); parsers.Put(typeof(decimal), SafeParse(decimal.Parse)); parsers.Put(typeof(decimal?), SafeParse(decimal.Parse)); parsers.Put(typeof(DateTime), SafeParse(DateTime.Parse)); parsers.Put(typeof(DateTime?), SafeParse(DateTime.Parse)); parsers.Put(typeof(TimeSpan), SafeParse(TimeSpan.Parse)); parsers.Put(typeof(TimeSpan?), SafeParse(TimeSpan.Parse)); parsers.Put(typeof(ZonedDateTime), SafeParse(ParseZonedDateTime)); parsers.Put(typeof(ZonedDateTime?), SafeParse(ParseZonedDateTime)); parsers.Put(typeof(LocalDate), SafeParse(ParseLocalDate)); parsers.Put(typeof(LocalDate?), SafeParse(ParseLocalDate)); parsers.Put(typeof(LocalTime), SafeParse(ParseLocalTime)); parsers.Put(typeof(LocalTime?), SafeParse(ParseLocalTime)); parsers.Put(typeof(IEnumerable), ImmutableListParse(value => value)); parsers.Put(typeof(ICollection), ImmutableListParse(value => value)); parsers.Put(typeof(IList), ImmutableListParse(value => value)); parsers.Put(typeof(List), ListParse(value => value)); parsers.Put(typeof(ISet), ImmutableListParse(value => value)); parsers.Put(typeof(HashSet), SetParse(value => value)); parsers.Put(typeof(IEnumerable), NullableImmutableListParse(bool.Parse)); parsers.Put(typeof(ICollection), NullableImmutableListParse(bool.Parse)); parsers.Put(typeof(IList), NullableImmutableListParse(bool.Parse)); parsers.Put(typeof(List), NullableListParse(bool.Parse)); parsers.Put(typeof(ISet), NullableImmutableSetParse(bool.Parse)); parsers.Put(typeof(HashSet), NullableSetParse(bool.Parse)); parsers.Put(typeof(IEnumerable), ImmutableListParse(byte.Parse)); parsers.Put(typeof(ICollection), ImmutableListParse(byte.Parse)); parsers.Put(typeof(IList), ImmutableListParse(byte.Parse)); parsers.Put(typeof(List), ListParse(byte.Parse)); parsers.Put(typeof(ISet), ImmutableSetParse(byte.Parse)); parsers.Put(typeof(HashSet), SetParse(byte.Parse)); parsers.Put(typeof(IEnumerable), ImmutableListParse(sbyte.Parse)); parsers.Put(typeof(ICollection), ImmutableListParse(sbyte.Parse)); parsers.Put(typeof(IList), ImmutableListParse(sbyte.Parse)); parsers.Put(typeof(List), ListParse(sbyte.Parse)); parsers.Put(typeof(ISet), ImmutableSetParse(sbyte.Parse)); parsers.Put(typeof(HashSet), SetParse(sbyte.Parse)); parsers.Put(typeof(IEnumerable), ImmutableListParse(short.Parse)); parsers.Put(typeof(ICollection), ImmutableListParse(short.Parse)); parsers.Put(typeof(IList), ImmutableListParse(short.Parse)); parsers.Put(typeof(List), ListParse(short.Parse)); parsers.Put(typeof(ISet), ImmutableSetParse(short.Parse)); parsers.Put(typeof(HashSet), SetParse(short.Parse)); parsers.Put(typeof(IEnumerable), ImmutableListParse(ushort.Parse)); parsers.Put(typeof(ICollection), ImmutableListParse(ushort.Parse)); parsers.Put(typeof(IList), ImmutableListParse(ushort.Parse)); parsers.Put(typeof(List), ListParse(ushort.Parse)); parsers.Put(typeof(ISet), ImmutableSetParse(ushort.Parse)); parsers.Put(typeof(HashSet), SetParse(ushort.Parse)); parsers.Put(typeof(IEnumerable), NullableImmutableListParse(int.Parse)); parsers.Put(typeof(ICollection), NullableImmutableListParse(int.Parse)); parsers.Put(typeof(IList), NullableImmutableListParse(int.Parse)); parsers.Put(typeof(List), NullableListParse(int.Parse)); parsers.Put(typeof(ISet), NullableImmutableSetParse(int.Parse)); parsers.Put(typeof(HashSet), NullableSetParse(int.Parse)); parsers.Put(typeof(IEnumerable), ImmutableListParse(uint.Parse)); parsers.Put(typeof(ICollection), ImmutableListParse(uint.Parse)); parsers.Put(typeof(IList), ImmutableListParse(uint.Parse)); parsers.Put(typeof(List), ListParse(uint.Parse)); parsers.Put(typeof(ISet), ImmutableSetParse(uint.Parse)); parsers.Put(typeof(HashSet), SetParse(uint.Parse)); parsers.Put(typeof(IEnumerable), NullableImmutableListParse(long.Parse)); parsers.Put(typeof(ICollection), NullableImmutableListParse(long.Parse)); parsers.Put(typeof(IList), NullableImmutableListParse(long.Parse)); parsers.Put(typeof(List), NullableListParse(long.Parse)); parsers.Put(typeof(ISet), NullableImmutableSetParse(long.Parse)); parsers.Put(typeof(HashSet), NullableSetParse(long.Parse)); parsers.Put(typeof(IEnumerable), ImmutableListParse(ulong.Parse)); parsers.Put(typeof(ICollection), ImmutableListParse(ulong.Parse)); parsers.Put(typeof(IList), ImmutableListParse(ulong.Parse)); parsers.Put(typeof(List), ListParse(ulong.Parse)); parsers.Put(typeof(ISet), ImmutableSetParse(ulong.Parse)); parsers.Put(typeof(HashSet), SetParse(ulong.Parse)); parsers.Put(typeof(IEnumerable), NullableImmutableListParse(float.Parse)); parsers.Put(typeof(ICollection), NullableImmutableListParse(float.Parse)); parsers.Put(typeof(IList), NullableImmutableListParse(float.Parse)); parsers.Put(typeof(List), NullableListParse(float.Parse)); parsers.Put(typeof(ISet), NullableImmutableSetParse(float.Parse)); parsers.Put(typeof(HashSet), NullableSetParse(float.Parse)); parsers.Put(typeof(IEnumerable), NullableImmutableListParse(double.Parse)); parsers.Put(typeof(ICollection), NullableImmutableListParse(double.Parse)); parsers.Put(typeof(IList), NullableImmutableListParse(double.Parse)); parsers.Put(typeof(List), NullableListParse(double.Parse)); parsers.Put(typeof(ISet), NullableImmutableSetParse(double.Parse)); parsers.Put(typeof(HashSet), NullableSetParse(double.Parse)); parsers.Put(typeof(IEnumerable), NullableImmutableListParse(decimal.Parse)); parsers.Put(typeof(ICollection), NullableImmutableListParse(decimal.Parse)); parsers.Put(typeof(IList), NullableImmutableListParse(decimal.Parse)); parsers.Put(typeof(List), NullableListParse(decimal.Parse)); parsers.Put(typeof(ISet), NullableImmutableSetParse(decimal.Parse)); parsers.Put(typeof(HashSet), NullableSetParse(decimal.Parse)); parsers.Put(typeof(IEnumerable), NullableImmutableListParse(DateTime.Parse)); parsers.Put(typeof(ICollection), NullableImmutableListParse(DateTime.Parse)); parsers.Put(typeof(IList), NullableImmutableListParse(DateTime.Parse)); parsers.Put(typeof(List), NullableListParse(DateTime.Parse)); parsers.Put(typeof(ISet), NullableImmutableSetParse(DateTime.Parse)); parsers.Put(typeof(HashSet), NullableSetParse(DateTime.Parse)); parsers.Put(typeof(IEnumerable), ImmutableListParse(TimeSpan.Parse)); parsers.Put(typeof(ICollection), ImmutableListParse(TimeSpan.Parse)); parsers.Put(typeof(IList), ImmutableListParse(TimeSpan.Parse)); parsers.Put(typeof(List), ListParse(TimeSpan.Parse)); parsers.Put(typeof(ISet), ImmutableSetParse(TimeSpan.Parse)); parsers.Put(typeof(HashSet), SetParse(TimeSpan.Parse)); return parsers.ToImmutableDictionary(); } private static Func SafeParse(Func parse) { return parameter => { try { return parse(parameter.Value); } catch (OverflowException) { throw ParameterOutOfRange(parameter, typeof(T)); } catch (FormatException) { throw InvalidParameterFormat(parameter, typeof(T)); } catch (Exception e) { throw new InvalidOperationException(Strings.Format("Unable to parse parameter: '{0}' with value: '{1}' to {2}", parameter.Name, parameter.Value, typeof(T)), e); } }; } private static Func NullableListParse(Func itemParser) where T: struct { return ListParse(it => it.ToNullable(itemParser)); } private static Func ListParse(Func itemParser) { return parameter => { if (string.IsNullOrEmpty(parameter.Value)) { return new List(); } return ParseCollection(parameter.Value, itemParser).ToList(); }; } private static Func NullableImmutableListParse(Func itemParser) where T: struct { return ImmutableListParse(it => it.ToNullable(itemParser)); } private static Func ImmutableListParse(Func itemParser) { return parameter => { if (string.IsNullOrEmpty(parameter.Value)) { return Lists.EmptyList(); } return ParseCollection(parameter.Value, itemParser).ToImmutableList(); }; } private static Func NullableSetParse(Func itemParser) where T: struct { return SetParse(it => it.ToNullable(itemParser)); } private static Func SetParse(Func itemParser) { return parameter => { if (string.IsNullOrEmpty(parameter.Value)) { return new HashSet(); } return ParseCollection(parameter.Value, itemParser).ToSet(); }; } private static Func NullableImmutableSetParse(Func itemParser) where T: struct { return ImmutableSetParse(it => it.ToNullable(itemParser)); } private static Func ImmutableSetParse(Func itemParser) { return parameter => { if (string.IsNullOrEmpty(parameter.Value)) { return Sets.EmptySet(); } return ParseCollection(parameter.Value, itemParser).ToImmutableHashSet(); }; } private static ZonedDateTime ParseZonedDateTime(string value) { var dateTime = DateTime.Parse(value); return new ZonedDateTime(Instant.FromDateTimeUtc(dateTime.ToUniversalTime()), DateTimeZone.Utc); } private static LocalDate ParseLocalDate(string value) { return LocalDatePattern.IsoPattern.Parse(value).Value; } private static LocalTime ParseLocalTime(string value) { return LocalTimePattern.ExtendedIsoPattern.Parse(value).Value; } private static ArgumentException ParameterOutOfRange(Parameter parameter, Type type) { return new ArgumentException(Strings.Format("Query: '{0}' value: '{1}' is out of range for: '{2}'", parameter.Name, parameter.Value, type)); } private static ArgumentException InvalidParameterFormat(Parameter parameter, Type type) { return new ArgumentException(Strings.Format("Query '{0}' value: '{1}' format is invalid for: '{2}'", parameter.Name, parameter.Value, type)); } private static IEnumerable ParseCollection(string value, Func itemParser) { var results = value.Split(new[] { ',' }, StringSplitOptions.None) .Where(it => it != null) .Select(it => it.Trim()) .Select(itemParser); return results; } public static T? ToNullable(this string s, Func itemParser) where T : struct { T? result = new T?(); try { if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0) { result = itemParser(s); } } catch (Exception e) { throw new InvalidOperationException(Strings.Format("Unable to parse value: '{0}' to nullable: '{1}'", s, typeof(T).ToString()), e); } return result; } private class Parameter { internal string Name { get; private set; } internal string Value { get; private set; } private Parameter(string name, string value) { Name = name; Value = value; } internal static Parameter Of(string name, string value) { return new Parameter(name, value); } } } internal enum ParameterType { Undefined, Query, Path, Header } }