| File: BuildCheck\API\CheckConfiguration.cs | Web Access |
| Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Experimental.BuildCheck.Utilities; namespace Microsoft.Build.Experimental.BuildCheck; /// <summary> /// Configuration for a build check. /// Default values can be specified by the Check in code. /// Users can overwrite the defaults by explicit settings in the .editorconfig file. /// Each rule can have its own configuration, which can differ per each project. /// The <see cref="EvaluationCheckScope"/> setting must be same for all rules in the same check (but can differ between projects) /// </summary> public class CheckConfiguration { // Defaults to be used if any configuration property is not specified neither as default // nor in the editorconfig configuration file. public static CheckConfiguration Default { get; } = new() { EvaluationCheckScope = BuildCheck.EvaluationCheckScope.ProjectFileOnly, Severity = CheckResultSeverity.None }; public static CheckConfiguration Null { get; } = new(); public string? RuleId { get; internal set; } /// <summary> /// This applies only to specific events, that can distinguish whether they are directly inferred from /// the current project, or from some import. If supported it can help tuning the level of detail or noise from check. /// /// If not supported by the data source - then the setting is ignored /// </summary> public EvaluationCheckScope? EvaluationCheckScope { get; init; } /// <summary> /// The severity of the result for the rule. /// </summary> public CheckResultSeverity? Severity { get; init; } /// <summary> /// Whether the check rule is enabled. /// If all rules within the check are not enabled, it will not be run. /// If some rules are enabled and some are not, the check will be run and reports will be post-filtered. /// </summary> public bool? IsEnabled { get { // Do not consider Default as enabled, because the default severity of the rule could be set to None if (Severity.HasValue && Severity.Value != CheckResultSeverity.Default) { return !Severity.Value.Equals(CheckResultSeverity.None); } return null; } } /// <summary> /// Creates a <see cref="CheckConfiguration"/> object based on the provided configuration dictionary. /// If the BuildCheckConfiguration's property name presented in the dictionary, the value of this key-value pair is parsed and assigned to the instance's field. /// If parsing failed the value will be equal to null. /// </summary> /// <param name="configDictionary">The configuration dictionary containing the settings for the build check. The configuration's keys are expected to be in lower case or the EqualityComparer to ignore case.</param> /// <returns>A new instance of <see cref="CheckConfiguration"/> with the specified settings.</returns> internal static CheckConfiguration Create(Dictionary<string, string>? configDictionary) => new() { EvaluationCheckScope = TryExtractEvaluationCheckScope(configDictionary), Severity = TryExtractSeverity(configDictionary), }; private static EvaluationCheckScope? TryExtractEvaluationCheckScope(Dictionary<string, string>? config) { if (!TryExtractValue(BuildCheckConstants.scopeConfigurationKey, config, out string? stringValue) || stringValue is null) { return null; } switch (stringValue) { case "projectfile": case "project_file": return BuildCheck.EvaluationCheckScope.ProjectFileOnly; case "work_tree_imports": return BuildCheck.EvaluationCheckScope.WorkTreeImports; case "all": return BuildCheck.EvaluationCheckScope.All; default: ThrowIncorrectValueException(BuildCheckConstants.scopeConfigurationKey, stringValue); break; } return null; } private static CheckResultSeverity? TryExtractSeverity(Dictionary<string, string>? config) { if (!TryExtractValue(BuildCheckConstants.severityConfigurationKey, config, out string? stringValue) || stringValue is null) { return null; } switch (stringValue) { case "none": return CheckResultSeverity.None; case "default": return CheckResultSeverity.Default; case "suggestion": return CheckResultSeverity.Suggestion; case "warning": return CheckResultSeverity.Warning; case "error": return CheckResultSeverity.Error; default: ThrowIncorrectValueException(BuildCheckConstants.severityConfigurationKey, stringValue); break; } return null; } private static bool TryExtractValue(string key, Dictionary<string, string>? config, out string? stringValue) { stringValue = null; if (config == null || !config.TryGetValue(key.ToLower(), out stringValue) || stringValue is null) { return false; } stringValue = stringValue.ToLower(); return true; } private static void ThrowIncorrectValueException(string key, string value) { // TODO: It will be nice to have the filename where the incorrect configuration was placed. throw new BuildCheckConfigurationException( $"Incorrect value provided in config for key {key}: '{value}'", buildCheckConfigurationErrorScope: BuildCheckConfigurationErrorScope.EditorConfigParser); } }