Address some static analysis and TODOs (#12)

This commit is contained in:
Stephen Toub 2025-03-20 23:09:38 -04:00 • committed by GitHub
commit 33b9582199
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 132 additions and 123 deletions

View file

@ -1,3 +1,5 @@
using ModelContextProtocol.Utils;
namespace System.Collections.Generic;
internal static class CollectionExtensions
@ -9,10 +11,7 @@ internal static class CollectionExtensions
public static TValue GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
if (dictionary is null)
{
throw new ArgumentNullException(nameof(dictionary));
}
Throw.IfNull(dictionary);
return dictionary.TryGetValue(key, out TValue? value) ? value : defaultValue;
}

View file

@ -0,0 +1,36 @@
using ModelContextProtocol.Utils;
using System.Buffers;
using System.Runtime.InteropServices;
namespace System.IO;
internal static class StreamExtensions
{
public static ValueTask WriteAsync(this Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
Throw.IfNull(stream);
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> segment))
{
return new ValueTask(stream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken));
}
else
{
return WriteAsyncCore(stream, buffer, cancellationToken);
static async ValueTask WriteAsyncCore(Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
byte[] array = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
buffer.Span.CopyTo(array);
await stream.WriteAsync(array, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
}
finally
{
ArrayPool<byte>.Shared.Return(array);
}
}
}
}
}

View file

@ -4,7 +4,21 @@ internal static class TextReaderExtensions
{
public static Task<string> ReadLineAsync(this TextReader reader, CancellationToken cancellationToken)
{
_ = cancellationToken;
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<string>(cancellationToken);
}
return reader.ReadLineAsync();
}
public static Task<string> ReadToEndAsync(this TextReader reader, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<string>(cancellationToken);
}
return reader.ReadToEndAsync();
}
}

View file

@ -1,3 +1,4 @@
using ModelContextProtocol.Utils;
using System.Runtime.InteropServices;
namespace System.IO;
@ -6,10 +7,7 @@ internal static class TextWriterExtensions
{
public static async Task WriteLineAsync(this TextWriter writer, ReadOnlyMemory<char> value, CancellationToken cancellationToken)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}
Throw.IfNull(writer);
if (value.IsEmpty)
{

View file

@ -1,13 +1,12 @@
using ModelContextProtocol.Utils;
namespace System.Net.Http;
internal static class HttpClientExtensions
{
public static async Task<Stream> ReadAsStreamAsync(this HttpContent content, CancellationToken cancellationToken)
{
if (content is null)
{
throw new ArgumentNullException(nameof(content));
}
Throw.IfNull(content);
cancellationToken.ThrowIfCancellationRequested();
return await content.ReadAsStreamAsync();
@ -15,10 +14,7 @@ internal static class HttpClientExtensions
public static async Task<string> ReadAsStringAsync(this HttpContent content, CancellationToken cancellationToken)
{
if (content is null)
{
throw new ArgumentNullException(nameof(content));
}
Throw.IfNull(content);
cancellationToken.ThrowIfCancellationRequested();
return await content.ReadAsStringAsync();

View file

@ -1,13 +1,12 @@
using ModelContextProtocol.Utils;
namespace System.Threading.Tasks;
internal static class CancellationTokenSourceExtensions
{
public static Task CancelAsync(this CancellationTokenSource cancellationTokenSource)
{
if (cancellationTokenSource is null)
{
throw new ArgumentNullException(nameof(cancellationTokenSource));
}
Throw.IfNull(cancellationTokenSource);
cancellationTokenSource.Cancel();
return Task.CompletedTask;

View file

@ -0,0 +1,17 @@
using System.Runtime.CompilerServices;
namespace System.Threading;
/// <summary>
/// await default(ForceYielding) to provide the same behavior as
/// await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding).
/// </summary>
internal readonly struct ForceYielding : INotifyCompletion, ICriticalNotifyCompletion
{
public ForceYielding GetAwaiter() => this;
public bool IsCompleted => false;
public void OnCompleted(Action continuation) => ThreadPool.QueueUserWorkItem(a => ((Action)a!)(), continuation);
public void UnsafeOnCompleted(Action continuation) => ThreadPool.UnsafeQueueUserWorkItem(a => ((Action)a!)(), continuation);
public void GetResult() { }
}

View file

@ -1,3 +1,5 @@
using ModelContextProtocol.Utils;
namespace System.Threading.Tasks;
internal static class TaskExtensions
@ -15,10 +17,7 @@ internal static class TaskExtensions
public static async Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken)
{
if (task is null)
{
throw new ArgumentNullException(nameof(task));
}
Throw.IfNull(task);
if (timeout < TimeSpan.Zero && timeout != Timeout.InfiniteTimeSpan)
{