< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Forms.KrPartWriteResult
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Forms/KrPartSinks.cs
Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589
Line coverage
100%
Covered lines: 3
Uncovered lines: 0
Coverable lines: 3
Total lines: 83
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 02/05/2026 - 00:28:18 Line coverage: 100% (3/3) Total lines: 83 Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_TempPath()100%11100%
get_Length()100%11100%
get_Sha256()100%11100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Forms/KrPartSinks.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2
 3namespace Kestrun.Forms;
 4
 5/// <summary>
 6/// Represents the result of writing a part to storage.
 7/// </summary>
 8public sealed record KrPartWriteResult
 9{
 10    /// <summary>
 11    /// Gets the path to the stored part.
 12    /// </summary>
 2113    public required string TempPath { get; init; }
 14
 15    /// <summary>
 16    /// Gets the length of the stored part in bytes.
 17    /// </summary>
 2018    public long Length { get; init; }
 19
 20    /// <summary>
 21    /// Gets the SHA-256 hash of the stored part, if computed.
 22    /// </summary>
 823    public string? Sha256 { get; init; }
 24}
 25
 26/// <summary>
 27/// Defines a streaming sink for multipart parts.
 28/// </summary>
 29public interface IKrPartSink
 30{
 31    /// <summary>
 32    /// Writes the part content to the sink.
 33    /// </summary>
 34    /// <param name="source">The source stream for the part.</param>
 35    /// <param name="cancellationToken">The cancellation token.</param>
 36    /// <returns>The write result.</returns>
 37    Task<KrPartWriteResult> WriteAsync(Stream source, CancellationToken cancellationToken);
 38}
 39
 40/// <summary>
 41/// Stores part contents on disk.
 42/// </summary>
 43public sealed class KrDiskPartSink(string destinationPath, bool computeSha256, string? fileName = null) : IKrPartSink
 44{
 45    private readonly string _destinationPath = destinationPath;
 46    private readonly bool _computeSha256 = computeSha256;
 47    private readonly string? _fileName = fileName;
 48
 49    /// <inheritdoc />
 50    public async Task<KrPartWriteResult> WriteAsync(Stream source, CancellationToken cancellationToken)
 51    {
 52        _ = Directory.CreateDirectory(_destinationPath);
 53        var fileName = string.IsNullOrWhiteSpace(_fileName)
 54            ? Path.GetRandomFileName()
 55            : _fileName;
 56        var uniqueName = string.IsNullOrWhiteSpace(_fileName)
 57            ? fileName
 58            : $"{Path.GetFileNameWithoutExtension(fileName)}-{Guid.NewGuid():N}{Path.GetExtension(fileName)}";
 59        var tempPath = Path.Combine(_destinationPath, uniqueName);
 60
 61        await using var fileStream = new FileStream(tempPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8192
 62
 63        using var hasher = _computeSha256 ? IncrementalHash.CreateHash(HashAlgorithmName.SHA256) : null;
 64        var buffer = new byte[81920];
 65        long total = 0;
 66        int read;
 67        while ((read = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0)
 68        {
 69            await fileStream.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);
 70            total += read;
 71            hasher?.AppendData(buffer, 0, read);
 72        }
 73
 74        var sha = hasher is null ? null : Convert.ToHexString(hasher.GetHashAndReset()).ToLowerInvariant();
 75
 76        return new KrPartWriteResult
 77        {
 78            TempPath = tempPath,
 79            Length = total,
 80            Sha256 = sha
 81        };
 82    }
 83}