Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:

- run: dotnet docfx docfx/docfx.json
name: 📚 Generate documentation
env:
DocFx: true # Workaround https://github.com/dotnet/docfx/issues/10808

- name: Upload artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docs_validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ jobs:
run: dotnet build -c Release src/StreamJsonRpc.Analyzers.CodeFixes
- name: 📚 Verify docfx build
run: dotnet docfx docfx/docfx.json --warningsAsErrors --disableGitFeatures
env:
DocFx: true # Workaround https://github.com/dotnet/docfx/issues/10808
3 changes: 3 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols Condition=" '$(DebugType)' != 'embedded' ">true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>

<!-- Workaround https://github.com/dotnet/docfx/issues/10808 -->
<DefineConstants Condition="'$(DocFx)'!='true'">$(DefineConstants);NOTDOCFX</DefineConstants>
</PropertyGroup>

<PropertyGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/StreamJsonRpc/JsonMessageFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,7 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer
/// Converts an enumeration token to an <see cref="IAsyncEnumerable{T}"/>.
/// </summary>
[RequiresDynamicCode(RuntimeReasons.CloseGenerics)]
[RequiresUnreferencedCode(RuntimeReasons.CloseGenerics)]
private class AsyncEnumerableConsumerConverter : JsonConverter
{
private static readonly MethodInfo ReadJsonOpenGenericMethod = typeof(AsyncEnumerableConsumerConverter).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).Single(m => m.Name == nameof(ReadJson) && m.IsGenericMethod);
Expand Down Expand Up @@ -1109,6 +1110,7 @@ private IAsyncEnumerable<T> ReadJson<T>(JsonReader reader, JsonSerializer serial
/// Converts an instance of <see cref="IAsyncEnumerable{T}"/> to an enumeration token.
/// </summary>
[RequiresDynamicCode(RuntimeReasons.CloseGenerics)]
[RequiresUnreferencedCode(RuntimeReasons.CloseGenerics)]
private class AsyncEnumerableGeneratorConverter : JsonConverter
{
private static readonly MethodInfo WriteJsonOpenGenericMethod = typeof(AsyncEnumerableGeneratorConverter).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Single(m => m.Name == nameof(WriteJson) && m.IsGenericMethod);
Expand Down
7 changes: 0 additions & 7 deletions src/StreamJsonRpc/RpcTargetMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,6 @@ public static RpcTargetMetadata FromShape<T, TProvider>()
where TProvider : IShapeable<T> => FromShape(TProvider.GetTypeShape());
#endif

/// <inheritdoc cref="FromShape(ITypeShape)" path="/summary"/>
/// <typeparam name="T">The type for which a shape should be obtained and <see cref="RpcTargetMetadata"/> generated for.</typeparam>
/// <param name="provider">The provider of type shapes from which to obtain the shape.</param>
/// <returns>An <see cref="RpcTargetMetadata"/> instance initialized from the shape of the <typeparamref name="T"/>.</returns>
[Obsolete("Use FromShape(ITypeShape) instead. This API will be removed soon.")]
public static RpcTargetMetadata FromShape<T>(ITypeShapeProvider provider) => FromShape(provider.GetTypeShapeOrThrow<T>());

/// <summary>
/// Creates an <see cref="RpcTargetMetadata"/> instance from the specified shape.
/// </summary>
Expand Down
60 changes: 60 additions & 0 deletions src/StreamJsonRpc/RpcTargetMetadataExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#if NOTDOCFX // Workaround https://github.com/dotnet/docfx/issues/10808

using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using PolyType.Abstractions;

namespace StreamJsonRpc;

/// <summary>
/// Extension methods for the <see cref="RpcTargetMetadata"/> class.
/// </summary>
public static class RpcTargetMetadataExtensions
{
extension(RpcTargetMetadata)
{
/// <summary>
/// Creates an <see cref="RpcTargetMetadata"/> instance from the specified shape.
/// </summary>
/// <typeparam name="T">The type for which a shape should be obtained and <see cref="RpcTargetMetadata"/> generated for.</typeparam>
/// <returns>An <see cref="RpcTargetMetadata"/> instance initialized from the shape of the <typeparamref name="T"/>.</returns>
#if NET8_0
[RequiresDynamicCode(ResolveDynamicMessage)]
#endif
#if NET
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use the RpcTargetMetadata.FromShape<T>() method instead. If using the extension method syntax, check that your type argument actually has a [GenerateShape] attribute or otherwise implements IShapeable<T> to avoid a runtime failure.", error: true)]
#endif
public static RpcTargetMetadata FromShape<T>()
=> RpcTargetMetadata.FromShape(TypeShapeResolver.ResolveDynamicOrThrow<T>());

/// <summary>
/// Creates an <see cref="RpcTargetMetadata"/> instance from the specified shape.
/// </summary>
/// <typeparam name="T">The type for which a shape should be obtained and <see cref="RpcTargetMetadata"/> generated for.</typeparam>
/// <typeparam name="TProvider">The provider of type shapes from which to obtain the shape.</typeparam>
/// <returns>An <see cref="RpcTargetMetadata"/> instance initialized from the shape of the <typeparamref name="T"/>.</returns>
#if NET8_0
[RequiresDynamicCode(ResolveDynamicMessage)]
#endif
#if NET
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use the RpcTargetMetadata.FromShape<T, TProvider>() method instead. If using the extension method syntax, check that your type argument actually has a [GenerateShape] attribute or otherwise implements IShapeable<T> to avoid a runtime failure.", error: true)]
#endif
public static RpcTargetMetadata FromShape<T, TProvider>()
=> RpcTargetMetadata.FromShape(TypeShapeResolver.ResolveDynamicOrThrow<T, TProvider>());
}

#if NET8_0
/// <summary>
/// A message to use as the argument to <see cref="RequiresDynamicCodeAttribute"/>
/// for methods that call into <see cref="TypeShapeResolver.ResolveDynamicOrThrow{T}"/>.
/// </summary>
/// <seealso href="https://github.com/dotnet/runtime/issues/119440#issuecomment-3269894751"/>
private const string ResolveDynamicMessage =
"Dynamic resolution of IShapeable<T> interface may require dynamic code generation in .NET 8 Native AOT. " +
"It is recommended to switch to statically resolved IShapeable<T> APIs or upgrade your app to .NET 9 or later.";
#endif
}

#endif
2 changes: 1 addition & 1 deletion src/StreamJsonRpc/StreamJsonRpc.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand Down
23 changes: 22 additions & 1 deletion test/StreamJsonRpc.Tests/RpcTargetMetadataTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,27 @@ internal partial interface IShapedContract
[Fact]
public void FromShape()
{
RpcTargetMetadata metadata = RpcTargetMetadata.FromShape(PolyType.SourceGenerator.TypeShapeProvider_StreamJsonRpc_Tests.Default.IShapedContract);
RpcTargetMetadata metadata = RpcTargetMetadata.FromShape<IShapedContract>();

var addAsync = Assert.Single(metadata.Methods["AddAsync"]);
var add = Assert.Single(metadata.AliasedMethods["Add"]);
Assert.Same(addAsync, add);

var subtract = Assert.Single(metadata.Methods["Subtract"]);
Assert.False(metadata.Methods.ContainsKey("SubtractAsync"));

// Verify that JsonRpcMethod.Name takes precedence over MethodShape.Name.
var multiply = Assert.Single(metadata.Methods["Times"]);

// Fail the test when support for events is added so we can update the test.
Assert.Equal(3, metadata.Methods.Count);
Assert.Single(metadata.AliasedMethods);
}

[Fact]
public void FromShape_TProvider()
{
RpcTargetMetadata metadata = RpcTargetMetadata.FromShape<IShapedContract, Witness>();

var addAsync = Assert.Single(metadata.Methods["AddAsync"]);
var add = Assert.Single(metadata.AliasedMethods["Add"]);
Expand Down Expand Up @@ -128,5 +148,6 @@ internal class RpcContractDerivedClass : IRpcContractDerived
}

[GenerateShapeFor<bool>]
[GenerateShapeFor<IShapedContract>]
private partial class Witness;
}
2 changes: 1 addition & 1 deletion test/StreamJsonRpc.Tests/StreamJsonRpc.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('Windows'))">$(TargetFrameworks);net472</TargetFrameworks>
<OutputType>exe</OutputType>
<RootNamespace />
Expand Down
Loading