File: Pipelines\DefaultPipelineOutputComparer.cs
Web Access
Project: src\src\Aspire.Hosting\Aspire.Hosting.csproj (Aspire.Hosting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#pragma warning disable ASPIREPIPELINES005 // Internal consumer of the experimental output diffing API.
 
namespace Aspire.Hosting.Pipelines;
 
/// <summary>
/// Compares captured output without retaining operation state.
/// </summary>
internal sealed class DefaultPipelineOutputComparer : IPipelineOutputComparer<PipelineOutputSnapshot>
{
    /// <inheritdoc />
    public Task<IReadOnlyList<PipelineOutputChange>> CompareAsync(
        PipelineOutputSnapshot before, PipelineOutputSnapshot after, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        ArgumentNullException.ThrowIfNull(before);
        ArgumentNullException.ThrowIfNull(after);
        return Task.FromResult(Compare(before.Files, after.Files, after.OutputDirectory));
    }
 
    /// <summary>
    /// Compares entry kinds, hashes, and available Git modes without accessing the filesystem.
    /// </summary>
    internal static IReadOnlyList<PipelineOutputChange> Compare(
        IReadOnlyDictionary<string, PipelineOutputFileSnapshot> before,
        IReadOnlyDictionary<string, PipelineOutputFileSnapshot> after,
        string outputDirectory)
    {
        ArgumentNullException.ThrowIfNull(before);
        ArgumentNullException.ThrowIfNull(after);
        var paths = before.Keys.Union(after.Keys, PipelineOutputRoot.PathComparer).Order(PipelineOutputRoot.PathComparer);
        var changes = new List<PipelineOutputChange>();
        foreach (var path in paths)
        {
            var hadBefore = before.TryGetValue(path, out var beforeSnapshot);
            var hasAfter = after.TryGetValue(path, out var afterSnapshot);
            if (!hadBefore)
            {
                changes.Add(new(PipelineOutputChangeKind.Added, Path.GetRelativePath(outputDirectory, path)));
            }
            else if (!hasAfter)
            {
                changes.Add(new(PipelineOutputChangeKind.Removed, Path.GetRelativePath(outputDirectory, path)));
            }
            else if (beforeSnapshot.IsLink != afterSnapshot.IsLink || !beforeSnapshot.Hash.Span.SequenceEqual(afterSnapshot.Hash.Span) ||
                beforeSnapshot.GitMode is not null && afterSnapshot.GitMode is not null && beforeSnapshot.GitMode != afterSnapshot.GitMode)
            {
                changes.Add(new(PipelineOutputChangeKind.Changed, Path.GetRelativePath(outputDirectory, path)));
            }
        }
 
        return changes.AsReadOnly();
    }
}
 
#pragma warning restore ASPIREPIPELINES005