File: BuildCheck\BuildCheckRuleTelemetryData.cs
Web Access
Project: ..\..\..\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace Microsoft.Build.Experimental.BuildCheck;
 
/// <summary>
/// Telemetry data for a single build check rule.
/// </summary>
/// <param name="ruleId"></param>
/// <param name="checkFriendlyName"></param>
/// <param name="isBuiltIn"></param>
/// <param name="defaultSeverity"></param>
internal sealed class BuildCheckRuleTelemetryData(
    string ruleId,
    string checkFriendlyName,
    bool isBuiltIn,
    DiagnosticSeverity defaultSeverity)
{
    public BuildCheckRuleTelemetryData(
        string ruleId,
        string checkFriendlyName,
        bool isBuiltIn,
        DiagnosticSeverity defaultSeverity,
        HashSet<DiagnosticSeverity> explicitSeverities,
        HashSet<string> projectNamesWhereEnabled,
        int violationMessagesCount,
        int violationWarningsCount,
        int violationErrorsCount,
        bool isThrottled,
        TimeSpan totalRuntime) : this(ruleId, checkFriendlyName, isBuiltIn,
        defaultSeverity)
    {
        ExplicitSeverities = explicitSeverities;
        ProjectNamesWhereEnabled = projectNamesWhereEnabled;
        ViolationMessagesCount = violationMessagesCount;
        ViolationWarningsCount = violationWarningsCount;
        ViolationErrorsCount = violationErrorsCount;
        IsThrottled = isThrottled;
        TotalRuntime = totalRuntime;
    }
 
    public static BuildCheckRuleTelemetryData Merge(
        BuildCheckRuleTelemetryData data1,
        BuildCheckRuleTelemetryData data2)
    {
        if (data1.RuleId != data2.RuleId)
        {
            throw new InvalidOperationException("Cannot merge telemetry data for different rules.");
        }
        return new BuildCheckRuleTelemetryData(
            data1.RuleId,
            data1.CheckFriendlyName,
            data1.IsBuiltIn,
            data1.DefaultSeverity,
            new HashSet<DiagnosticSeverity>(data1.ExplicitSeverities.Union(data2.ExplicitSeverities)),
            new HashSet<string>(data1.ProjectNamesWhereEnabled.Union(data2.ProjectNamesWhereEnabled)),
            data1.ViolationMessagesCount + data2.ViolationMessagesCount,
            data1.ViolationWarningsCount + data2.ViolationWarningsCount,
            data1.ViolationErrorsCount + data2.ViolationErrorsCount,
            data1.IsThrottled || data2.IsThrottled,
            data1.TotalRuntime + data2.TotalRuntime);
    }
 
    public string RuleId { get; init; } = ruleId;
    public string CheckFriendlyName { get; init; } = checkFriendlyName;
    public bool IsBuiltIn { get; init; } = isBuiltIn;
    public DiagnosticSeverity DefaultSeverity { get; init; } = defaultSeverity;
 
    /// <summary>
    /// A set of explicitly set severities (through editorconfig(s)) for the rule. There can be multiple - as different projects can have different settings.
    /// </summary>
    public HashSet<DiagnosticSeverity> ExplicitSeverities { get; init; } = [];
    public HashSet<string> ProjectNamesWhereEnabled { get; init; } = [];
    public int ViolationMessagesCount { get; private set; }
    public int ViolationWarningsCount { get; private set; }
    public int ViolationErrorsCount { get; private set; }
    public int ViolationsCount => ViolationMessagesCount + ViolationWarningsCount + ViolationErrorsCount;
    public bool IsThrottled { get; private set; }
    public TimeSpan TotalRuntime { get; set; }
 
    public void IncrementMessagesCount() => ViolationMessagesCount++;
    public void IncrementWarningsCount() => ViolationWarningsCount++;
    public void IncrementErrorsCount() => ViolationErrorsCount++;
    public void SetThrottled() => IsThrottled = true;
}