Skip to content

Commit 75c32f9

Browse files
authored
Improve UrlFormatter (#108)
1 parent 8ff4a66 commit 75c32f9

File tree

2 files changed

+86
-32
lines changed

2 files changed

+86
-32
lines changed

src/Elastic.Transport/Extensions/Extensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ internal static void ThrowIfNull<T>(this T value, string name) where T : class
3838
throw new ArgumentNullException(name);
3939
}
4040

41-
internal static bool IsNullOrEmpty(this string value) => string.IsNullOrEmpty(value);
41+
internal static bool IsNullOrEmpty(this string? value) => string.IsNullOrEmpty(value);
4242

4343
internal static string Utf8String(this byte[] bytes) => bytes == null ? null : Encoding.UTF8.GetString(bytes, 0, bytes.Length);
4444

src/Elastic.Transport/Requests/UrlFormatter.cs

Lines changed: 85 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
// See the LICENSE file in the project root for more information
44

55
using System;
6-
using System.Collections.Generic;
7-
using System.Linq;
6+
using System.Collections;
87
using System.Runtime.Serialization;
8+
using System.Text;
99
using Elastic.Transport.Extensions;
1010

1111
namespace Elastic.Transport;
@@ -22,59 +22,113 @@ public sealed class UrlFormatter : IFormatProvider, ICustomFormatter
2222
public UrlFormatter(ITransportConfiguration settings) => _settings = settings;
2323

2424
/// <inheritdoc cref="ICustomFormatter.Format"/>>
25-
public string Format(string format, object arg, IFormatProvider formatProvider)
25+
public string Format(string? format, object? arg, IFormatProvider? formatProvider)
2626
{
2727
if (arg == null) throw new ArgumentNullException();
2828

29-
if (format == "r") return arg.ToString();
29+
if (format == "r") return arg.ToString() ?? string.Empty;
3030

3131
var value = CreateString(arg, _settings);
3232
if (value.IsNullOrEmpty() && !format.IsNullOrEmpty())
3333
throw new ArgumentException($"The parameter: {format} to the url is null or empty");
3434

35-
return value.IsNullOrEmpty() ? string.Empty : Uri.EscapeDataString(value);
35+
return string.IsNullOrEmpty(value) ? string.Empty : Uri.EscapeDataString(value);
3636
}
3737

3838
/// <inheritdoc cref="IFormatProvider.GetFormat"/>
39-
public object GetFormat(Type formatType) => formatType == typeof(ICustomFormatter) ? this : null;
39+
public object? GetFormat(Type formatType) => formatType == typeof(ICustomFormatter) ? this : null;
4040

4141
/// <inheritdoc cref="CreateString(object, ITransportConfiguration)"/>
42-
public string CreateString(object value) => CreateString(value, _settings);
42+
public string? CreateString(object? value) => CreateString(value, _settings);
4343

4444
/// <summary> Creates a query string representation for <paramref name="value"/> </summary>
45-
public static string? CreateString(object? value, ITransportConfiguration settings)
45+
public static string? CreateString(object? value, ITransportConfiguration settings) =>
46+
value switch
47+
{
48+
null => null,
49+
string s => s,
50+
string[] ss => string.Join(",", ss),
51+
Enum e => e.GetStringValue(),
52+
bool b => b ? "true" : "false",
53+
DateTimeOffset offset => offset.ToString("o"),
54+
TimeSpan timeSpan => timeSpan.ToTimeUnit(),
55+
// Special handling to support non-zero based arrays
56+
Array pns => CreateStringFromArray(pns, settings),
57+
// Performance optimization for directly indexable collections
58+
IList pns => CreateStringFromIList(pns, settings),
59+
// Generic implementation for all other collections
60+
IEnumerable pns => CreateStringFromIEnumerable(pns, settings),
61+
_ => ResolveUrlParameterOrDefault(value, settings)
62+
};
63+
64+
private static string CreateStringFromArray(Array value, ITransportConfiguration settings)
4665
{
47-
switch (value)
66+
switch (value.Length)
67+
{
68+
case 0:
69+
return string.Empty;
70+
case 1:
71+
return ResolveUrlParameterOrDefault(value.GetValue(value.GetLowerBound(0)), settings);
72+
}
73+
74+
var sb = new StringBuilder();
75+
76+
for (var i = value.GetLowerBound(0); i <= value.GetUpperBound(0); ++i)
4877
{
49-
case null: return null;
50-
case string s: return s;
51-
case string[] ss: return string.Join(",", ss);
52-
case Enum e: return e.GetStringValue();
53-
case bool b: return b ? "true" : "false";
54-
case DateTimeOffset offset: return offset.ToString("o");
55-
case IEnumerable<object> pns:
56-
return CreateStringFromIEnumerable(pns, settings);
57-
58-
case Array pns:
59-
return CreateStringFromIEnumerable(ConvertArrayToEnumerable(pns), settings);
60-
61-
case TimeSpan timeSpan: return timeSpan.ToTimeUnit();
62-
default:
63-
return ResolveUrlParameterOrDefault(value, settings);
78+
if (sb.Length != 0)
79+
sb.Append(',');
80+
81+
sb.Append(ResolveUrlParameterOrDefault(value.GetValue(i), settings));
6482
}
83+
84+
return sb.ToString();
6585
}
6686

67-
private static string CreateStringFromIEnumerable(IEnumerable<object> value, ITransportConfiguration settings) =>
68-
string.Join(",", value.Select(o => ResolveUrlParameterOrDefault(o, settings)));
87+
private static string CreateStringFromIList(IList value, ITransportConfiguration settings)
88+
{
89+
switch (value.Count)
90+
{
91+
case 0:
92+
return string.Empty;
93+
case 1:
94+
return ResolveUrlParameterOrDefault(value[0], settings);
95+
}
96+
97+
var sb = new StringBuilder();
6998

70-
private static IEnumerable<object> ConvertArrayToEnumerable(Array array)
99+
for (var i = 0; i < value.Count; ++i)
100+
{
101+
if (sb.Length != 0)
102+
sb.Append(',');
103+
104+
sb.Append(ResolveUrlParameterOrDefault(value[i], settings));
105+
}
106+
107+
return sb.ToString();
108+
}
109+
110+
private static string CreateStringFromIEnumerable(IEnumerable value, ITransportConfiguration settings)
71111
{
72-
for (var i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
73-
yield return array.GetValue(i);
112+
var sb = new StringBuilder();
113+
114+
foreach (var v in value)
115+
{
116+
if (sb.Length != 0)
117+
sb.Append(',');
118+
119+
sb.Append(ResolveUrlParameterOrDefault(v, settings));
120+
}
121+
122+
return sb.ToString();
74123
}
75124

76-
private static string ResolveUrlParameterOrDefault(object value, ITransportConfiguration settings) =>
77-
value is IUrlParameter urlParam ? urlParam.GetString(settings) : GetEnumMemberName(value) ?? value.ToString();
125+
private static string ResolveUrlParameterOrDefault(object? value, ITransportConfiguration settings) =>
126+
value switch
127+
{
128+
null => string.Empty,
129+
IUrlParameter urlParam => urlParam.GetString(settings),
130+
_ => GetEnumMemberName(value) ?? value.ToString() ?? string.Empty
131+
};
78132

79133
private static string? GetEnumMemberName(object value)
80134
{

0 commit comments

Comments
 (0)