Skip to content

Commit 8be665c

Browse files
committed
Remove LINQ usage from OpenAPI comparers
1 parent 102f4bd commit 8be665c

6 files changed

+127
-24
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.OpenApi;
5+
6+
internal static class ComparerHelpers
7+
{
8+
internal static bool DictionaryEquals<TKey, TValue>(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y, IEqualityComparer<TValue> comparer)
9+
where TKey : notnull
10+
where TValue : notnull
11+
{
12+
if (x.Keys.Count != y.Keys.Count)
13+
{
14+
return false;
15+
}
16+
17+
foreach (var key in x.Keys)
18+
{
19+
if (!y.TryGetValue(key, out var value) || !comparer.Equals(x[key], value))
20+
{
21+
return false;
22+
}
23+
}
24+
25+
return true;
26+
}
27+
28+
internal static bool ListEquals<T>(IList<T> x, IList<T> y, IEqualityComparer<T> comparer)
29+
{
30+
if (x.Count != y.Count)
31+
{
32+
return false;
33+
}
34+
35+
for (var i = 0; i < x.Count; i++)
36+
{
37+
if (!comparer.Equals(x[i], y[i]))
38+
{
39+
return false;
40+
}
41+
}
42+
43+
return true;
44+
}
45+
46+
internal static bool ByteArrayEquals(byte[] x, byte[] y)
47+
{
48+
if (x.Length != y.Length)
49+
{
50+
return false;
51+
}
52+
53+
for (var i = 0; i < x.Length; i++)
54+
{
55+
if (!Equals(x[i], y[i]))
56+
{
57+
return false;
58+
}
59+
}
60+
61+
return true;
62+
}
63+
}

src/OpenApi/src/Comparers/OpenApiAnyComparer.cs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Linq;
54
using Microsoft.OpenApi.Any;
5+
using Microsoft.OpenApi.Interfaces;
66

77
namespace Microsoft.AspNetCore.OpenApi;
88

9-
internal sealed class OpenApiAnyComparer : IEqualityComparer<IOpenApiAny>
9+
internal sealed class OpenApiAnyComparer : IEqualityComparer<IOpenApiAny>, IEqualityComparer<IOpenApiExtension>
1010
{
1111
public static OpenApiAnyComparer Instance { get; } = new OpenApiAnyComparer();
1212

@@ -29,23 +29,48 @@ public bool Equals(IOpenApiAny? x, IOpenApiAny? y)
2929
(x switch
3030
{
3131
OpenApiNull _ => y is OpenApiNull,
32-
OpenApiArray arrayX => y is OpenApiArray arrayY && arrayX.SequenceEqual(arrayY, Instance),
33-
OpenApiObject objectX => y is OpenApiObject objectY && objectX.Keys.Count == objectY.Keys.Count && objectX.Keys.All(key => objectY.ContainsKey(key) && Equals(objectX[key], objectY[key])),
34-
OpenApiBinary binaryX => y is OpenApiBinary binaryY && binaryX.Value.SequenceEqual(binaryY.Value),
32+
OpenApiArray arrayX => y is OpenApiArray arrayY && ComparerHelpers.ListEquals(arrayX, arrayY, Instance),
33+
OpenApiObject objectX => y is OpenApiObject objectY && ComparerHelpers.DictionaryEquals(objectX, objectY, Instance),
34+
OpenApiBinary binaryX => y is OpenApiBinary binaryY && ComparerHelpers.ByteArrayEquals(binaryX.Value, binaryY.Value),
3535
OpenApiInteger integerX => y is OpenApiInteger integerY && integerX.Value == integerY.Value,
3636
OpenApiLong longX => y is OpenApiLong longY && longX.Value == longY.Value,
3737
OpenApiDouble doubleX => y is OpenApiDouble doubleY && doubleX.Value == doubleY.Value,
3838
OpenApiFloat floatX => y is OpenApiFloat floatY && floatX.Value == floatY.Value,
3939
OpenApiBoolean booleanX => y is OpenApiBoolean booleanY && booleanX.Value == booleanY.Value,
4040
OpenApiString stringX => y is OpenApiString stringY && stringX.Value == stringY.Value,
4141
OpenApiPassword passwordX => y is OpenApiPassword passwordY && passwordX.Value == passwordY.Value,
42-
OpenApiByte byteX => y is OpenApiByte byteY && byteX.Value.SequenceEqual(byteY.Value),
42+
OpenApiByte byteX => y is OpenApiByte byteY && ComparerHelpers.ByteArrayEquals(byteX.Value, byteY.Value),
4343
OpenApiDate dateX => y is OpenApiDate dateY && dateX.Value == dateY.Value,
4444
OpenApiDateTime dateTimeX => y is OpenApiDateTime dateTimeY && dateTimeX.Value == dateTimeY.Value,
4545
_ => x.Equals(y)
4646
});
4747
}
4848

49+
public bool Equals(IOpenApiExtension? x, IOpenApiExtension? y)
50+
{
51+
if (x is null && y is null)
52+
{
53+
return true;
54+
}
55+
56+
if (x is null || y is null)
57+
{
58+
return false;
59+
}
60+
61+
if (object.ReferenceEquals(x, y))
62+
{
63+
return true;
64+
}
65+
66+
if (x is IOpenApiAny openApiAnyX && y is IOpenApiAny openApiAnyY)
67+
{
68+
return Equals(openApiAnyX, openApiAnyY);
69+
}
70+
71+
return false;
72+
}
73+
4974
public int GetHashCode(IOpenApiAny obj)
5075
{
5176
var hashCode = new HashCode();
@@ -78,4 +103,14 @@ public int GetHashCode(IOpenApiAny obj)
78103

79104
return hashCode.ToHashCode();
80105
}
106+
107+
public int GetHashCode(IOpenApiExtension obj)
108+
{
109+
if (obj is IOpenApiAny any)
110+
{
111+
return GetHashCode(any);
112+
}
113+
114+
return obj.GetHashCode();
115+
}
81116
}

src/OpenApi/src/Comparers/OpenApiDiscriminatorComparer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Linq;
54
using Microsoft.OpenApi.Models;
65

76
namespace Microsoft.AspNetCore.OpenApi;
@@ -27,7 +26,7 @@ public bool Equals(OpenApiDiscriminator? x, OpenApiDiscriminator? y)
2726

2827
return x.PropertyName == y.PropertyName &&
2928
x.Mapping.Count == y.Mapping.Count &&
30-
x.Mapping.Keys.All(key => y.Mapping.ContainsKey(key) && x.Mapping[key] == y.Mapping[key]);
29+
ComparerHelpers.DictionaryEquals(x.Mapping, y.Mapping, StringComparer.Ordinal);
3130
}
3231

3332
public int GetHashCode(OpenApiDiscriminator obj)

src/OpenApi/src/Comparers/OpenApiExternalDocsComparer.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Linq;
54
using Microsoft.OpenApi.Models;
65

76
namespace Microsoft.AspNetCore.OpenApi;
@@ -27,8 +26,8 @@ public bool Equals(OpenApiExternalDocs? x, OpenApiExternalDocs? y)
2726

2827
return x.Description == y.Description &&
2928
x.Url == y.Url &&
30-
x.Extensions.Count == y.Extensions.Count
31-
&& x.Extensions.Keys.All(k => y.Extensions.ContainsKey(k) && y.Extensions[k] == x.Extensions[k]);
29+
x.Extensions.Count == y.Extensions.Count &&
30+
ComparerHelpers.DictionaryEquals(x.Extensions, y.Extensions, OpenApiAnyComparer.Instance);
3231
}
3332

3433
public int GetHashCode(OpenApiExternalDocs obj)

src/OpenApi/src/Comparers/OpenApiSchemaComparer.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Linq;
5-
using Microsoft.OpenApi.Any;
64
using Microsoft.OpenApi.Models;
75

86
namespace Microsoft.AspNetCore.OpenApi;
@@ -28,19 +26,19 @@ public bool Equals(OpenApiSchema? x, OpenApiSchema? y)
2826

2927
return Instance.Equals(x.AdditionalProperties, y.AdditionalProperties) &&
3028
x.AdditionalPropertiesAllowed == y.AdditionalPropertiesAllowed &&
31-
x.AllOf.SequenceEqual(y.AllOf, Instance) &&
32-
x.AnyOf.SequenceEqual(y.AnyOf, Instance) &&
29+
ComparerHelpers.ListEquals(x.AllOf, y.AllOf, Instance) &&
30+
ComparerHelpers.ListEquals(x.AnyOf, y.AnyOf, Instance) &&
3331
x.Deprecated == y.Deprecated &&
3432
OpenApiAnyComparer.Instance.Equals(x.Default, y.Default) &&
3533
x.Description == y.Description &&
3634
OpenApiDiscriminatorComparer.Instance.Equals(x.Discriminator, y.Discriminator) &&
3735
OpenApiAnyComparer.Instance.Equals(x.Example, y.Example) &&
3836
x.ExclusiveMaximum == y.ExclusiveMaximum &&
3937
x.ExclusiveMinimum == y.ExclusiveMinimum &&
40-
x.Extensions.Count == y.Extensions.Count
41-
&& x.Extensions.Keys.All(k => y.Extensions.ContainsKey(k) && x.Extensions[k] is IOpenApiAny anyX && y.Extensions[k] is IOpenApiAny anyY && OpenApiAnyComparer.Instance.Equals(anyX, anyY)) &&
38+
x.Extensions.Count == y.Extensions.Count &&
39+
ComparerHelpers.DictionaryEquals(x.Extensions, y.Extensions, OpenApiAnyComparer.Instance) &&
4240
OpenApiExternalDocsComparer.Instance.Equals(x.ExternalDocs, y.ExternalDocs) &&
43-
x.Enum.SequenceEqual(y.Enum, OpenApiAnyComparer.Instance) &&
41+
ComparerHelpers.ListEquals(x.Enum, y.Enum, OpenApiAnyComparer.Instance) &&
4442
x.Format == y.Format &&
4543
Instance.Equals(x.Items, y.Items) &&
4644
x.Title == y.Title &&
@@ -54,20 +52,30 @@ public bool Equals(OpenApiSchema? x, OpenApiSchema? y)
5452
x.MinLength == y.MinLength &&
5553
x.MinProperties == y.MinProperties &&
5654
x.MultipleOf == y.MultipleOf &&
57-
x.OneOf.SequenceEqual(y.OneOf, Instance) &&
55+
ComparerHelpers.ListEquals(x.OneOf, y.OneOf, Instance) &&
5856
Instance.Equals(x.Not, y.Not) &&
5957
x.Nullable == y.Nullable &&
6058
x.Pattern == y.Pattern &&
61-
x.Properties.Keys.All(k => y.Properties.ContainsKey(k) && Instance.Equals(x.Properties[k], y.Properties[k])) &&
59+
ComparerHelpers.DictionaryEquals(x.Properties, y.Properties, Instance) &&
6260
x.ReadOnly == y.ReadOnly &&
63-
x.Required.Order().SequenceEqual(y.Required.Order()) &&
61+
RequiredEquals(x.Required, y.Required) &&
6462
OpenApiReferenceComparer.Instance.Equals(x.Reference, y.Reference) &&
6563
x.UniqueItems == y.UniqueItems &&
6664
x.UnresolvedReference == y.UnresolvedReference &&
6765
x.WriteOnly == y.WriteOnly &&
6866
OpenApiXmlComparer.Instance.Equals(x.Xml, y.Xml);
6967
}
7068

69+
internal static bool RequiredEquals(ISet<string> x, ISet<string> y)
70+
{
71+
if (x.Count != y.Count)
72+
{
73+
return false;
74+
}
75+
76+
return x.SetEquals(y);
77+
}
78+
7179
public int GetHashCode(OpenApiSchema obj)
7280
{
7381
var hashCode = new HashCode();

src/OpenApi/src/Comparers/OpenApiXmlComparer.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Linq;
54
using Microsoft.OpenApi.Models;
65

76
namespace Microsoft.AspNetCore.OpenApi;
@@ -30,8 +29,8 @@ public bool Equals(OpenApiXml? x, OpenApiXml? y)
3029
x.Prefix == y.Prefix &&
3130
x.Attribute == y.Attribute &&
3231
x.Wrapped == y.Wrapped &&
33-
x.Extensions.Count == y.Extensions.Count
34-
&& x.Extensions.Keys.All(k => y.Extensions.ContainsKey(k) && y.Extensions[k] == x.Extensions[k]);
32+
x.Extensions.Count == y.Extensions.Count &&
33+
ComparerHelpers.DictionaryEquals(x.Extensions, y.Extensions, OpenApiAnyComparer.Instance);
3534
}
3635

3736
public int GetHashCode(OpenApiXml obj)

0 commit comments

Comments
 (0)