forked from eiriktsarpalis/PolyType
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
TypeShapeProvider.Combine
static method
Closes eiriktsarpalis#91
- Loading branch information
Showing
4 changed files
with
185 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using PolyType.Abstractions; | ||
|
||
namespace PolyType; | ||
|
||
/// <summary> | ||
/// Provides behaviors for manipulating or implementing <see cref="ITypeShapeProvider"/> instances. | ||
/// </summary> | ||
public static class TypeShapeProvider | ||
{ | ||
/// <summary> | ||
/// Creates a new <see cref="ITypeShapeProvider"/> that returns the first shape found | ||
/// from iterating over a given list of providers. | ||
/// </summary> | ||
/// <param name="providers">The sequence of providers to solicit for type shapes.</param> | ||
/// <returns>The aggregating <see cref="ITypeShapeProvider"/>.</returns> | ||
public static ITypeShapeProvider Combine(params ReadOnlySpan<ITypeShapeProvider> providers) | ||
{ | ||
return new AggregatingTypeShapeProvider(providers.ToArray()); | ||
} | ||
|
||
private sealed class AggregatingTypeShapeProvider(ITypeShapeProvider[] providers) : ITypeShapeProvider | ||
{ | ||
public ITypeShape? GetShape(Type type) | ||
{ | ||
foreach (ITypeShapeProvider provider in providers) | ||
{ | ||
if (provider.GetShape(type) is ITypeShape shape) | ||
{ | ||
return shape; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
using System.Reflection; | ||
|
||
namespace PolyType.Tests; | ||
|
||
public class TypeShapeProviderStaticTests | ||
{ | ||
[Fact] | ||
public void Combine_EmptyList() | ||
{ | ||
ITypeShapeProvider aggregate = TypeShapeProvider.Combine(); | ||
Assert.NotNull(aggregate); | ||
Assert.Null(aggregate.GetShape(typeof(int))); | ||
} | ||
|
||
[Fact] | ||
public void Combine_One_Null() | ||
{ | ||
ITypeShapeProvider aggregate = TypeShapeProvider.Combine(new MockTypeShapeProvider()); | ||
Assert.Null(aggregate.GetShape(typeof(int))); | ||
} | ||
|
||
|
||
[Fact] | ||
public void Combine_One_NonNull() | ||
{ | ||
MockTypeShapeProvider first = new() | ||
{ | ||
Shapes = | ||
{ | ||
[typeof(int)] = new MockTypeShape<int>(), | ||
}, | ||
}; | ||
ITypeShapeProvider aggregate = TypeShapeProvider.Combine(first); | ||
Assert.Same(first.Shapes[typeof(int)], aggregate.GetShape(typeof(int))); | ||
} | ||
|
||
[Fact] | ||
public void Combine_Many() | ||
{ | ||
MockTypeShapeProvider first = new() | ||
{ | ||
Shapes = | ||
{ | ||
[typeof(int)] = new MockTypeShape<int>(), | ||
}, | ||
}; | ||
MockTypeShapeProvider second = new() | ||
{ | ||
Shapes = | ||
{ | ||
[typeof(string)] = new MockTypeShape<int>(), | ||
}, | ||
}; | ||
ITypeShapeProvider aggregate = TypeShapeProvider.Combine(first, second); | ||
Assert.Same(first.Shapes[typeof(int)], aggregate.GetShape(typeof(int))); | ||
Assert.Same(second.Shapes[typeof(string)], aggregate.GetShape(typeof(string))); | ||
Assert.Null(aggregate.GetShape(typeof(bool))); | ||
} | ||
|
||
/// <summary> | ||
/// Validates that the aggregating provider does not cache results. | ||
/// </summary> | ||
/// <remarks> | ||
/// Whether it caches or not is something of an arbitrary design decision. | ||
/// This test is to ensure that the behavior is documented and intentional. | ||
/// </remarks> | ||
[Fact] | ||
public void CachePolicy_ShapeNotCached() | ||
{ | ||
MockTypeShapeProvider first = new() | ||
{ | ||
Shapes = | ||
{ | ||
[typeof(int)] = new MockTypeShape<int>(), | ||
}, | ||
}; | ||
ITypeShapeProvider aggregate = TypeShapeProvider.Combine(first); | ||
Assert.Same(first.Shapes[typeof(int)], aggregate.GetShape(typeof(int))); | ||
|
||
// Remove the shape, and verify that the aggregator no longer returns it. | ||
first.Shapes.Remove(typeof(int)); | ||
Assert.Null(aggregate.GetShape(typeof(int))); | ||
} | ||
|
||
/// <summary> | ||
/// Validates that the aggregating provider does not cache the provider that was used previously. | ||
/// </summary> | ||
/// <remarks> | ||
/// Whether it caches or not is something of an arbitrary design decision. | ||
/// This test is to ensure that the behavior is documented and intentional. | ||
/// </remarks> | ||
[Fact] | ||
public void CachePolicy_ShapeProviderNotCached() | ||
{ | ||
MockTypeShapeProvider first = new() | ||
{ | ||
Shapes = | ||
{ | ||
[typeof(int)] = new MockTypeShape<int>(), | ||
}, | ||
}; | ||
MockTypeShapeProvider second = new() | ||
{ | ||
Shapes = | ||
{ | ||
[typeof(string)] = new MockTypeShape<string>(), | ||
}, | ||
}; | ||
ITypeShapeProvider aggregate = TypeShapeProvider.Combine(first, second); | ||
Assert.NotNull(aggregate.GetShape(typeof(string))); | ||
|
||
// Add a new string shape to the first provider. | ||
first.Shapes[typeof(string)] = new MockTypeShape<string>(); | ||
|
||
// The aggregator should return the new shape. | ||
Assert.Same(first.Shapes[typeof(string)], aggregate.GetShape(typeof(string))); | ||
} | ||
|
||
private class MockTypeShapeProvider : ITypeShapeProvider | ||
{ | ||
internal Dictionary<Type, ITypeShape> Shapes { get; } = new(); | ||
|
||
public ITypeShape? GetShape(Type type) => this.Shapes.TryGetValue(type, out ITypeShape? shape) ? shape : null; | ||
} | ||
|
||
private class MockTypeShape<T> : ITypeShape<T> | ||
{ | ||
public Type Type => throw new NotImplementedException(); | ||
|
||
public TypeShapeKind Kind => throw new NotImplementedException(); | ||
|
||
public ITypeShapeProvider Provider => throw new NotImplementedException(); | ||
|
||
public ICustomAttributeProvider? AttributeProvider => throw new NotImplementedException(); | ||
|
||
public object? Accept(ITypeShapeVisitor visitor, object? state = null) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public object? Invoke(ITypeShapeFunc func, object? state = null) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
global using PolyType.Abstractions; | ||
global using Xunit; |