File: MarkdownTemplate.cs
Web Access
Project: src\tools\AnalyzeCiFailure\AnalyzeCiFailure.csproj (AnalyzeCiFailure)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text.RegularExpressions;
 
namespace AnalyzeCiFailure;
 
/// <summary>Renders embedded Markdown layouts without interpreting substituted diagnostic text as template syntax.</summary>
internal static partial class MarkdownTemplate
{
    internal static string Render(string name, params (string Name, string Value)[] values)
    {
        using var stream = typeof(MarkdownTemplate).Assembly.GetManifestResourceStream($"AnalyzeCiFailure.Templates.{name}.md.tmpl")
            ?? throw new InvalidOperationException($"Embedded Markdown template '{name}' was not found.");
        using var reader = new StreamReader(stream);
        var template = reader.ReadToEnd().ReplaceLineEndings("\n");
        var replacements = values.ToDictionary(value => value.Name, value => value.Value, StringComparer.Ordinal);
        var used = new HashSet<string>(StringComparer.Ordinal);
 
        // Placeholders are ##UPPER_CASE_NAME##. Replace only the original template's
        // matches: diagnostic text containing another placeholder must remain literal.
        var rendered = NamedPlaceholderRegex().Replace(
            template,
            match =>
        {
            var placeholder = match.Groups["name"].Value;
            if (!replacements.TryGetValue(placeholder, out var value))
            {
                throw new InvalidOperationException($"Markdown template '{name}' requires placeholder '{placeholder}'.");
            }
            used.Add(placeholder);
            return value;
        });
        foreach (var placeholder in replacements.Keys)
        {
            if (!used.Contains(placeholder))
            {
                throw new InvalidOperationException($"Markdown template '{name}' does not contain placeholder '{placeholder}'.");
            }
        }
 
        return rendered;
    }
 
    [GeneratedRegex("##(?<name>[A-Z][A-Z0-9_]*)##")]
    private static partial Regex NamedPlaceholderRegex();
}