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
4 changes: 2 additions & 2 deletions src/StreamJsonRpc.Analyzers/GeneratorModels/InterfaceModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace StreamJsonRpc.Analyzers.GeneratorModels;
/// <param name="Methods">The methods in the interface.</param>
/// <param name="Events">The events in the interface.</param>
/// <param name="HasUnsupportedMemberTypes">Indicates whether the interface has additional members that are not supported.</param>
internal record InterfaceModel(string FullName, string Name, ImmutableEquatableArray<string> TypeParameters, Container? Container, ImmutableEquatableArray<MethodModel> Methods, ImmutableEquatableArray<EventModel> Events, bool HasUnsupportedMemberTypes)
internal record InterfaceModel(string FullName, string Name, ImmutableEquatableArray<(VarianceKind Variance, string Identifier)> TypeParameters, Container? Container, ImmutableEquatableArray<MethodModel> Methods, ImmutableEquatableArray<EventModel> Events, bool HasUnsupportedMemberTypes)
{
internal required bool IsPartial { get; init; }

Expand Down Expand Up @@ -77,7 +77,7 @@ internal static InterfaceModel Create(INamedTypeSymbol iface, KnownSymbols symbo
return new InterfaceModel(
iface.ToDisplayString(ProxyGenerator.FullyQualifiedNoGlobalWithNullableFormat),
iface.Name,
[.. iface.TypeParameters.Select(tp => tp.Name)],
[.. iface.TypeParameters.Select(tp => (tp.Variance, tp.Name))],
Container.CreateFor((INamespaceOrTypeSymbol?)iface.ContainingType ?? iface.ContainingNamespace, cancellationToken),
methods.ToImmutableEquatableArray(),
events.ToImmutableEquatableArray(),
Expand Down
10 changes: 9 additions & 1 deletion src/StreamJsonRpc.Analyzers/GeneratorModels/ProxyModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ internal ProxyModel(ImmutableEquatableSet<InterfaceModel> interfaces, string? ex
internal void WriteInterfaceMapping(SourceWriter writer, InterfaceModel iface)
{
string genericTypeParameters = iface.TypeParameters.Length > 0
? $"<{string.Join(", ", iface.TypeParameters)}>"
? $"<{string.Join(", ", iface.TypeParameters.Select(WriteTypeParameter))}>"
: string.Empty;
writer.WriteLine($$"""
[global::StreamJsonRpc.Reflection.JsonRpcProxyMappingAttribute(typeof({{ProxyGenerator.GenerationNamespace}}.{{this.Name}}{{this.GenericTypeDefinitionSuffix}}))]
Expand Down Expand Up @@ -291,4 +291,12 @@ private static string CreateProxyName(ImmutableEquatableSet<InterfaceModel> inte
string additionalInterfaceHashString = Convert.ToBase64String(additionalInterfaceHash).TrimEnd('=').Replace('+', '_').Replace('/', '_');
return $"{sorted[0]}{additionalInterfaceHashString[..8]}";
}

private static string WriteTypeParameter((VarianceKind Variance, string Identifier) typeParameter) => typeParameter.Variance switch
{
VarianceKind.None => typeParameter.Identifier,
VarianceKind.In => $"in {typeParameter.Identifier}",
VarianceKind.Out => $"out {typeParameter.Identifier}",
_ => throw new InvalidOperationException($"Unknown variance kind: {typeParameter.Variance}."),
};
}
24 changes: 24 additions & 0 deletions test/StreamJsonRpc.Analyzer.Tests/ProxyGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,30 @@ public partial interface IGenericMarshalable<T>
""");
}

[Fact]
public async Task RpcMarshalable_Generic_WithInModifier()
{
await VerifyCS.RunDefaultAsync("""
[RpcMarshalable]
public partial interface IGenericMarshalable<in T>
{
Task DoSomethingWithParameterAsync(T parameter);
}
""");
}

[Fact]
public async Task RpcMarshalable_Generic_WithOutModifier()
{
await VerifyCS.RunDefaultAsync("""
[RpcMarshalable]
public partial interface IGenericMarshalable<out T>
{
Task DoSomethingWithParameterAsync();
}
""");
}

[Fact]
public async Task RpcMarshalable_GenericWithClosedPrescriptions()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// <auto-generated/>

#nullable enable
#pragma warning disable CS0436 // prefer local types to imported ones

[global::StreamJsonRpc.Reflection.JsonRpcProxyMappingAttribute(typeof(StreamJsonRpc.Generated.IGenericMarshalable_Proxy<>))]
partial interface IGenericMarshalable<in T>
{
}

namespace StreamJsonRpc.Generated
{

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("StreamJsonRpc.Analyzers", "x.x.x.x")]
internal class IGenericMarshalable_Proxy<T> : global::StreamJsonRpc.Reflection.ProxyBase
, global::IGenericMarshalable<T>
{

private static readonly global::System.Collections.Generic.IReadOnlyDictionary<string, global::System.Type> DoSomethingWithParameterAsyncNamedArgumentDeclaredTypes1 = new global::System.Collections.Generic.Dictionary<string, global::System.Type>
{
["parameter"] = typeof(T),
};

private static readonly global::System.Collections.Generic.IReadOnlyList<global::System.Type> DoSomethingWithParameterAsyncPositionalArgumentDeclaredTypes1 = new global::System.Collections.Generic.List<global::System.Type>
{
typeof(T),
};

private string? transformedDoSomethingWithParameterAsync1;

public IGenericMarshalable_Proxy(global::StreamJsonRpc.JsonRpc client, global::StreamJsonRpc.Reflection.ProxyInputs inputs)
: base(client, inputs)
{
}

global::System.Threading.Tasks.Task global::IGenericMarshalable<T>.DoSomethingWithParameterAsync(T parameter)
{
if (this.IsDisposed) throw new global::System.ObjectDisposedException(this.GetType().FullName);

this.OnCallingMethod("DoSomethingWithParameterAsync");
string rpcMethodName = this.transformedDoSomethingWithParameterAsync1 ??= this.TransformMethodName("DoSomethingWithParameterAsync", typeof(global::IGenericMarshalable<T>));
global::System.Threading.Tasks.Task result = this.Options.ServerRequiresNamedArguments ?
this.JsonRpc.InvokeWithParameterObjectAsync(rpcMethodName, ConstructNamedArgs(), DoSomethingWithParameterAsyncNamedArgumentDeclaredTypes1, default) :
this.JsonRpc.InvokeWithCancellationAsync(rpcMethodName, [parameter], DoSomethingWithParameterAsyncPositionalArgumentDeclaredTypes1, default);
this.OnCalledMethod("DoSomethingWithParameterAsync");

return result;

global::System.Collections.Generic.Dictionary<string, object?> ConstructNamedArgs()
=> new()
{
["parameter"] = parameter,
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// <auto-generated/>

#nullable enable
#pragma warning disable CS0436 // prefer local types to imported ones

[global::StreamJsonRpc.Reflection.JsonRpcProxyMappingAttribute(typeof(StreamJsonRpc.Generated.IGenericMarshalable_Proxy<>))]
partial interface IGenericMarshalable<out T>
{
}

namespace StreamJsonRpc.Generated
{

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("StreamJsonRpc.Analyzers", "x.x.x.x")]
internal class IGenericMarshalable_Proxy<T> : global::StreamJsonRpc.Reflection.ProxyBase
, global::IGenericMarshalable<T>
{

private static readonly global::System.Collections.Generic.IReadOnlyDictionary<string, global::System.Type> DoSomethingWithParameterAsyncNamedArgumentDeclaredTypes1 = new global::System.Collections.Generic.Dictionary<string, global::System.Type>
{
};

private static readonly global::System.Collections.Generic.IReadOnlyList<global::System.Type> DoSomethingWithParameterAsyncPositionalArgumentDeclaredTypes1 = new global::System.Collections.Generic.List<global::System.Type>
{
};

private string? transformedDoSomethingWithParameterAsync1;

public IGenericMarshalable_Proxy(global::StreamJsonRpc.JsonRpc client, global::StreamJsonRpc.Reflection.ProxyInputs inputs)
: base(client, inputs)
{
}

global::System.Threading.Tasks.Task global::IGenericMarshalable<T>.DoSomethingWithParameterAsync()
{
if (this.IsDisposed) throw new global::System.ObjectDisposedException(this.GetType().FullName);

this.OnCallingMethod("DoSomethingWithParameterAsync");
string rpcMethodName = this.transformedDoSomethingWithParameterAsync1 ??= this.TransformMethodName("DoSomethingWithParameterAsync", typeof(global::IGenericMarshalable<T>));
global::System.Threading.Tasks.Task result = this.Options.ServerRequiresNamedArguments ?
this.JsonRpc.InvokeWithParameterObjectAsync(rpcMethodName, ConstructNamedArgs(), DoSomethingWithParameterAsyncNamedArgumentDeclaredTypes1, default) :
this.JsonRpc.InvokeWithCancellationAsync(rpcMethodName, [], DoSomethingWithParameterAsyncPositionalArgumentDeclaredTypes1, default);
this.OnCalledMethod("DoSomethingWithParameterAsync");

return result;

global::System.Collections.Generic.Dictionary<string, object?> ConstructNamedArgs()
=> new()
{
};
}
}
}
Loading