Skip to content

Commit

Permalink
Merge pull request #698 from Cysharp/feature/CSharp9Nullable
Browse files Browse the repository at this point in the history
MagicOnion.Client targets C# 9 and enable nullable annotations
  • Loading branch information
mayuki authored Oct 26, 2023
2 parents 49f626a + 5c0fa5c commit 6330993
Show file tree
Hide file tree
Showing 105 changed files with 1,265 additions and 559 deletions.
6 changes: 5 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<Project>
<PropertyGroup>
<VersionPrefix>5.1.8</VersionPrefix>
</PropertyGroup>

<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<VersionPrefix>5.1.8</VersionPrefix>
<WarningsAsErrors>$(WarningsAsErrors);Nullable</WarningsAsErrors>
</PropertyGroup>

<!-- NuGet Packaging -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions samples/JwtAuthentication/JwtAuthApp.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@ public async ValueTask<ResponseContext> SendAsync(RequestContext context, Func<R

AuthenticationTokenStorage.Current.Update(authResult.Token, authResult.Expiration); // NOTE: You can also read the token expiration date from JWT.

context.CallOptions.Headers.Remove(new Metadata.Entry("Authorization", string.Empty));
context.CallOptions.Headers?.Remove(new Metadata.Entry("Authorization", string.Empty));
}

if (!context.CallOptions.Headers.Contains(new Metadata.Entry("Authorization", string.Empty)))
if (!context.CallOptions.Headers?.Contains(new Metadata.Entry("Authorization", string.Empty)) ?? false)
{
context.CallOptions.Headers.Add("Authorization", "Bearer " + AuthenticationTokenStorage.Current.Token);
context.CallOptions.Headers?.Add("Authorization", "Bearer " + AuthenticationTokenStorage.Current.Token);
}

return await next(context);
Expand All @@ -144,7 +144,7 @@ class AuthenticationTokenStorage

private readonly object _syncObject = new object();

public string Token { get; private set; }
public string? Token { get; private set; }
public DateTimeOffset Expiration { get; private set; }

public bool IsExpired => Token == null || Expiration < DateTimeOffset.Now;
Expand Down
6 changes: 6 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<Project>
<Import Project="..\Directory.Build.props" />

<!-- LangVersion of Client, Abstractions, Shared must match supported Unity version-->
<PropertyGroup>
<LangVersion>latest</LangVersion>
<_LangVersionUnityBaseline>9.0</_LangVersionUnityBaseline>
</PropertyGroup>

<PropertyGroup>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\MagicOnion\opensource.snk</AssemblyOriginatorKeyFile>
Expand Down
17 changes: 10 additions & 7 deletions src/MagicOnion.Abstractions/ClientStreamingResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ namespace MagicOnion
/// </summary>
public struct ClientStreamingResult<TRequest, TResponse> : IDisposable
{
internal readonly TResponse rawValue;
internal readonly TResponse? rawValue;
internal readonly bool hasRawValue;
readonly IAsyncClientStreamingCallWrapper<TRequest, TResponse> inner;
readonly IAsyncClientStreamingCallWrapper<TRequest, TResponse>? inner;

public ClientStreamingResult(TResponse rawValue)
{
Expand All @@ -33,23 +33,26 @@ public ClientStreamingResult(IAsyncClientStreamingCallWrapper<TRequest, TRespons
this.inner = inner;
}

IAsyncClientStreamingCallWrapper<TRequest, TResponse> GetRequiredInner()
=> inner ?? throw new NotSupportedException("ClientStreamingResult has no inner stream.");

/// <summary>
/// Asynchronous call result.
/// </summary>
public Task<TResponse> ResponseAsync
=> hasRawValue ? Task.FromResult(rawValue) : inner.ResponseAsync;
=> hasRawValue ? Task.FromResult(rawValue!) : GetRequiredInner().ResponseAsync;

/// <summary>
/// Asynchronous access to response headers.
/// </summary>
public Task<Metadata> ResponseHeadersAsync
=> inner.ResponseHeadersAsync;
=> GetRequiredInner().ResponseHeadersAsync;

/// <summary>
/// Async stream to send streaming requests.
/// </summary>
public IClientStreamWriter<TRequest> RequestStream
=> inner?.RequestStream;
=> GetRequiredInner().RequestStream;

/// <summary>
/// Allows awaiting this object directly.
Expand All @@ -65,14 +68,14 @@ public TaskAwaiter<TResponse> GetAwaiter()
/// Throws InvalidOperationException otherwise.
/// </summary>
public Status GetStatus()
=> inner.GetStatus();
=> GetRequiredInner().GetStatus();

/// <summary>
/// Gets the call trailing metadata if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Metadata GetTrailers()
=> inner.GetTrailers();
=> GetRequiredInner().GetTrailers();

/// <summary>
/// Provides means to cleanup after the call.
Expand Down
5 changes: 4 additions & 1 deletion src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>

<LangVersion>$(_LangVersionUnityBaseline)</LangVersion>
<Nullable>enable</Nullable>

<DefineConstants>$(DefineConstants);NON_UNITY</DefineConstants>

<PackageId>MagicOnion.Abstractions</PackageId>
Expand Down
66 changes: 40 additions & 26 deletions src/MagicOnion.Abstractions/UnaryResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace MagicOnion
public readonly struct UnaryResult
{
internal readonly bool hasRawValue;
internal readonly Task rawTaskValue;
internal readonly Task<IResponseContext<Nil>> response;
internal readonly Task? rawTaskValue;
internal readonly Task<IResponseContext<Nil>>? response;

public UnaryResult(Nil nil)
{
Expand Down Expand Up @@ -74,20 +74,24 @@ public Task ResponseAsync
/// </summary>
public Task<Metadata> ResponseHeadersAsync => UnwrapResponseHeaders();

Task<IResponseContext<Nil>> GetRequiredResponse()
=> response ?? throw new InvalidOperationException("UnaryResult has no response.");

async Task UnwrapResponse()
{
var ctx = await response.ConfigureAwait(false);
var ctx = await GetRequiredResponse().ConfigureAwait(false);
await ctx.ResponseAsync.ConfigureAwait(false);
}

async Task<Metadata> UnwrapResponseHeaders()
{
var ctx = await response.ConfigureAwait(false);
var ctx = await GetRequiredResponse().ConfigureAwait(false);
return await ctx.ResponseHeadersAsync.ConfigureAwait(false);
}

IResponseContext TryUnwrap()
{
var response = GetRequiredResponse();
if (!response.IsCompleted)
{
throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this.");
Expand Down Expand Up @@ -128,21 +132,24 @@ public Metadata GetTrailers()
/// </remarks>
public void Dispose()
{
if (!response.IsCompleted)
if (response is not null)
{
UnwrapDispose();
}
else
{
response.Result.Dispose();
if (!response.IsCompleted)
{
UnwrapDispose();
}
else
{
response.Result.Dispose();
}
}
}

async void UnwrapDispose()
{
try
{
var ctx = await response.ConfigureAwait(false);
var ctx = await GetRequiredResponse().ConfigureAwait(false);
ctx.Dispose();
}
catch
Expand Down Expand Up @@ -182,10 +189,10 @@ public static UnaryResult<Nil> Nil
public readonly struct UnaryResult<TResponse>
{
internal readonly bool hasRawValue; // internal
internal readonly TResponse rawValue; // internal
internal readonly Task<TResponse> rawTaskValue; // internal
internal readonly TResponse? rawValue; // internal
internal readonly Task<TResponse>? rawTaskValue; // internal

readonly Task<IResponseContext<TResponse>> response;
readonly Task<IResponseContext<TResponse>>? response;

public UnaryResult(TResponse rawValue)
{
Expand Down Expand Up @@ -224,18 +231,18 @@ public Task<TResponse> ResponseAsync
// So, we will return the default value of TResponse as Task.
if (response is null)
{
return Task.FromResult(default(TResponse));
return Task.FromResult(default(TResponse)!);
}

return UnwrapResponse();
}
else if (rawTaskValue != null)
else if (rawTaskValue is not null)
{
return rawTaskValue;
}
else
{
return Task.FromResult(rawValue);
return Task.FromResult(rawValue!);
}
}
}
Expand All @@ -245,23 +252,26 @@ public Task<TResponse> ResponseAsync
/// </summary>
public Task<Metadata> ResponseHeadersAsync => UnwrapResponseHeaders();

Task<IResponseContext<TResponse>> GetRequiredResponse()
=> response ?? throw new InvalidOperationException("UnaryResult has no response.");

async Task<TResponse> UnwrapResponse()
{
var ctx = await response.ConfigureAwait(false);
var ctx = await GetRequiredResponse().ConfigureAwait(false);
return await ctx.ResponseAsync.ConfigureAwait(false);
}

async Task<Metadata> UnwrapResponseHeaders()
{
var ctx = await response.ConfigureAwait(false);
var ctx = await GetRequiredResponse().ConfigureAwait(false);
return await ctx.ResponseHeadersAsync.ConfigureAwait(false);
}

async void UnwrapDispose()
{
try
{
var ctx = await response.ConfigureAwait(false);
var ctx = await GetRequiredResponse().ConfigureAwait(false);
ctx.Dispose();
}
catch
Expand All @@ -271,6 +281,7 @@ async void UnwrapDispose()

IResponseContext<TResponse> TryUnwrap()
{
var response = GetRequiredResponse();
if (!response.IsCompleted)
{
throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this.");
Expand Down Expand Up @@ -311,13 +322,16 @@ public Metadata GetTrailers()
/// </remarks>
public void Dispose()
{
if (!response.IsCompleted)
if (response is not null)
{
UnwrapDispose();
}
else
{
response.Result.Dispose();
if (!response.IsCompleted)
{
UnwrapDispose();
}
else
{
response.Result.Dispose();
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>

<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>

<!-- Source Generator / Roslyn Analyzer -->
<IsRoslynComponent>true</IsRoslynComponent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>

<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>

<!-- Source Generator / Roslyn Analyzer -->
<IsRoslynComponent>true</IsRoslynComponent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ namespace MagicOnion
/// </summary>
public struct ClientStreamingResult<TRequest, TResponse> : IDisposable
{
internal readonly TResponse rawValue;
internal readonly TResponse? rawValue;
internal readonly bool hasRawValue;
readonly IAsyncClientStreamingCallWrapper<TRequest, TResponse> inner;
readonly IAsyncClientStreamingCallWrapper<TRequest, TResponse>? inner;

public ClientStreamingResult(TResponse rawValue)
{
Expand All @@ -33,23 +33,26 @@ public ClientStreamingResult(IAsyncClientStreamingCallWrapper<TRequest, TRespons
this.inner = inner;
}

IAsyncClientStreamingCallWrapper<TRequest, TResponse> GetRequiredInner()
=> inner ?? throw new NotSupportedException("ClientStreamingResult has no inner stream.");

/// <summary>
/// Asynchronous call result.
/// </summary>
public Task<TResponse> ResponseAsync
=> hasRawValue ? Task.FromResult(rawValue) : inner.ResponseAsync;
=> hasRawValue ? Task.FromResult(rawValue!) : GetRequiredInner().ResponseAsync;

/// <summary>
/// Asynchronous access to response headers.
/// </summary>
public Task<Metadata> ResponseHeadersAsync
=> inner.ResponseHeadersAsync;
=> GetRequiredInner().ResponseHeadersAsync;

/// <summary>
/// Async stream to send streaming requests.
/// </summary>
public IClientStreamWriter<TRequest> RequestStream
=> inner?.RequestStream;
=> GetRequiredInner().RequestStream;

/// <summary>
/// Allows awaiting this object directly.
Expand All @@ -65,14 +68,14 @@ public TaskAwaiter<TResponse> GetAwaiter()
/// Throws InvalidOperationException otherwise.
/// </summary>
public Status GetStatus()
=> inner.GetStatus();
=> GetRequiredInner().GetStatus();

/// <summary>
/// Gets the call trailing metadata if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Metadata GetTrailers()
=> inner.GetTrailers();
=> GetRequiredInner().GetTrailers();

/// <summary>
/// Provides means to cleanup after the call.
Expand Down
Loading

0 comments on commit 6330993

Please sign in to comment.