|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text.Json;
namespace Microsoft.AspNetCore.StaticWebAssets.Tasks;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
public partial class StaticWebAssetsManifest : IEquatable<StaticWebAssetsManifest>
{
internal static StaticWebAssetsManifest Create(
string source,
string basePath,
string mode,
string manifestType,
ReferencedProjectConfiguration[] referencedProjectConfigurations,
StaticWebAssetsDiscoveryPattern[] discoveryPatterns,
StaticWebAsset[] assets,
StaticWebAssetEndpoint[] endpoints)
{
var result = new StaticWebAssetsManifest()
{
Version = 1,
Source = source,
BasePath = basePath,
Mode = mode,
ManifestType = manifestType,
ReferencedProjectsConfiguration = referencedProjectConfigurations,
DiscoveryPatterns = discoveryPatterns,
Assets = assets,
Endpoints = endpoints
};
result.Hash = result.ComputeManifestHash();
return result;
}
private string ComputeManifestHash()
{
using var stream = new MemoryStream();
var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { SkipValidation = true });
JsonSerializer.Serialize(writer, Source);
JsonSerializer.Serialize(writer, BasePath);
JsonSerializer.Serialize(writer, Mode);
JsonSerializer.Serialize(writer, ManifestType);
JsonSerializer.Serialize(writer, ReferencedProjectsConfiguration);
JsonSerializer.Serialize(writer, DiscoveryPatterns);
JsonSerializer.Serialize(writer, Assets);
JsonSerializer.Serialize(writer, Endpoints);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
using var sha256 = SHA256.Create();
return Convert.ToBase64String(sha256.ComputeHash(stream));
}
internal bool IsPublishManifest() => ManifestTypes.IsPublish(ManifestType);
internal bool IsCurrentProjectAsset(StaticWebAsset asset) => asset.HasSourceId(Source);
public int Version { get; set; } = 1;
public string Hash { get; set; }
public string Source { get; set; }
public string BasePath { get; set; }
public string Mode { get; set; }
public string ManifestType { get; set; }
public ReferencedProjectConfiguration[] ReferencedProjectsConfiguration { get; set; }
public StaticWebAssetsDiscoveryPattern[] DiscoveryPatterns { get; set; }
public StaticWebAsset[] Assets { get; set; }
public StaticWebAssetEndpoint[] Endpoints { get; set; }
public static StaticWebAssetsManifest FromJsonBytes(byte[] jsonBytes)
{
var manifest = JsonSerializer.Deserialize(jsonBytes, StaticWebAssetsJsonSerializerContext.RelaxedEscaping.StaticWebAssetsManifest);
if (manifest.Version != 1)
{
throw new InvalidOperationException($"Invalid manifest version. Expected manifest version '1' and found version '{manifest.Version}'.");
}
return manifest;
}
public static StaticWebAssetsManifest FromStream(Stream stream)
{
// Unfortunately the Stream overloads are not available on .net 472 so we need to do it this way.
// That said, it doesn't matter since this method is only used for test purposes.
using var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
var manifest = JsonSerializer.Deserialize<StaticWebAssetsManifest>(memoryStream.ToArray());
if (manifest.Version != 1)
{
throw new InvalidOperationException($"Invalid manifest version. Expected manifest version '1' and found version '{manifest.Version}'.");
}
return manifest;
}
public static StaticWebAssetsManifest FromJsonString(string jsonManifest)
{
var manifest = JsonSerializer.Deserialize<StaticWebAssetsManifest>(jsonManifest);
if (manifest.Version != 1)
{
throw new InvalidOperationException($"Invalid manifest version. Expected manifest version '1' and found version '{manifest.Version}'.");
}
return manifest;
}
public override bool Equals(object obj) => Equals(obj as StaticWebAssetsManifest);
public bool Equals(StaticWebAssetsManifest other) =>
other != null
&& Version == other.Version
&& Hash == other.Hash
&& Source == other.Source
&& BasePath == other.BasePath
&& Mode == other.Mode
&& ManifestType == other.ManifestType
&& EqualityComparer<ReferencedProjectConfiguration[]>.Default.Equals(ReferencedProjectsConfiguration, other.ReferencedProjectsConfiguration)
&& EqualityComparer<StaticWebAssetsDiscoveryPattern[]>.Default.Equals(DiscoveryPatterns, other.DiscoveryPatterns)
&& EqualityComparer<StaticWebAsset[]>.Default.Equals(Assets, other.Assets)
&& EqualityComparer<StaticWebAssetEndpoint[]>.Default.Equals(Endpoints, other.Endpoints);
public override int GetHashCode()
{
#if NET6_0_OR_GREATER
var hash = new HashCode();
hash.Add(Version);
hash.Add(Hash);
hash.Add(Source);
hash.Add(BasePath);
hash.Add(Mode);
hash.Add(ManifestType);
hash.Add(ReferencedProjectsConfiguration);
hash.Add(DiscoveryPatterns);
hash.Add(Assets);
hash.Add(Endpoints);
return hash.ToHashCode();
#else
var hashCode = 1467594941;
hashCode = hashCode * -1521134295 + Version.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Hash);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Source);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(BasePath);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Mode);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(ManifestType);
hashCode = hashCode * -1521134295 + EqualityComparer<ReferencedProjectConfiguration[]>.Default.GetHashCode(ReferencedProjectsConfiguration);
hashCode = hashCode * -1521134295 + EqualityComparer<StaticWebAssetsDiscoveryPattern[]>.Default.GetHashCode(DiscoveryPatterns);
hashCode = hashCode * -1521134295 + EqualityComparer<StaticWebAsset[]>.Default.GetHashCode(Assets);
hashCode = hashCode * -1521134295 + EqualityComparer<StaticWebAssetEndpoint[]>.Default.GetHashCode(Endpoints);
return hashCode;
#endif
}
public class ManifestTypes
{
public const string Build = nameof(Build);
public const string Publish = nameof(Publish);
public static bool IsPublish(string manifestType) =>
IsType(Publish, manifestType);
public static bool IsType(string manifestType, string candidate) =>
string.Equals(manifestType, candidate, StringComparison.Ordinal);
}
public class ManifestModes
{
public const string Default = nameof(Default);
public const string Root = nameof(Root);
public const string SelfContained = nameof(SelfContained);
internal static bool IsDefault(string projectMode) =>
string.Equals(projectMode, Default, StringComparison.Ordinal);
internal static bool IsRoot(string projectMode) =>
string.Equals(Root, projectMode, StringComparison.Ordinal);
internal static bool ShouldIncludeAssetInCurrentProject(StaticWebAsset asset, string projectMode) => IsRoot(projectMode) && !asset.IsForReferencedProjectsOnly();
internal static bool ShouldIncludeAssetAsReference(StaticWebAsset asset, string projectMode) =>
IsRoot(projectMode) && !asset.IsForReferencedProjectsOnly() ||
IsDefault(projectMode) && !asset.IsForCurrentProjectOnly();
}
private static readonly JsonSerializerOptions _debuggerDisplayJsonOptions = new() { WriteIndented = true };
private string GetDebuggerDisplay() => JsonSerializer.Serialize(this, _debuggerDisplayJsonOptions);
}
|