File: NodeMode.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.Diagnostics.CodeAnalysis;
using System.Globalization;
 
namespace Microsoft.Build.Framework
{
    /// <summary>
    /// Enumeration of the various node modes that MSBuild.exe can run in.
    /// </summary>
    internal enum NodeMode
    {
        /// <summary>
        /// Normal out-of-process node.
        /// </summary>
        OutOfProcNode = 1,
 
        /// <summary>
        /// Out-of-process task host node.
        /// </summary>
        OutOfProcTaskHostNode = 2,
 
        /// <summary>
        /// Out-of-process RAR (ResolveAssemblyReference) service node.
        /// </summary>
        OutOfProcRarNode = 3,
 
        /// <summary>
        /// Out-of-process server node.
        /// </summary>
        OutOfProcServerNode = 8,
    }
 
    /// <summary>
    /// Helper methods for the NodeMode enum.
    /// </summary>
    internal static partial class NodeModeHelper
    {
        /// <summary>
        /// Converts a NodeMode value to a command line argument string.
        /// </summary>
        /// <param name="nodeMode">The node mode to convert</param>
        /// <returns>The command line argument string (e.g., "/nodemode:1")</returns>
        public static string ToCommandLineArgument(NodeMode nodeMode) => $"/nodemode:{(int)nodeMode}";
 
        /// <summary>
        /// Tries to parse a node mode value from a string, supporting both integer values and enum names (case-insensitive).
        /// </summary>
        /// <param name="value">The value to parse (can be an integer or enum name)</param>
        /// <param name="nodeMode">The parsed NodeMode value if successful</param>
        /// <returns>True if parsing succeeded, false otherwise</returns>
        public static bool TryParse(string value, [NotNullWhen(true)] out NodeMode? nodeMode)
        {
#if NET
            return TryParseImpl(value.AsSpan(), out nodeMode);
#else
            return TryParseImpl(value, out nodeMode);
#endif
        }
 
#if NET
        /// <summary>
        /// Tries to parse a node mode value from a span, supporting both integer values and enum names (case-insensitive).
        /// </summary>
        /// <param name="value">The value to parse (can be an integer or enum name)</param>
        /// <param name="nodeMode">The parsed NodeMode value if successful</param>
        /// <returns>True if parsing succeeded, false otherwise</returns>
        public static bool TryParse(ReadOnlySpan<char> value, [NotNullWhen(true)] out NodeMode? nodeMode)
        {
            return TryParseImpl(value, out nodeMode);
        }
#endif
 
#if NET
        private static bool TryParseImpl(ReadOnlySpan<char> value, [NotNullWhen(true)] out NodeMode? nodeMode)
#else
        private static bool TryParseImpl(string value, [NotNullWhen(true)] out NodeMode? nodeMode)
#endif
        {
            nodeMode = null;
 
#if NET
            if (value.IsEmpty || value.IsWhiteSpace())
            {
                return false;
            }
#else
            if (string.IsNullOrWhiteSpace(value))
            {
                return false;
            }
#endif
 
            // First try to parse as an integer for backward compatibility
            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int intValue))
            {
                // Validate that the integer corresponds to a valid enum value
                if (Enum.IsDefined(typeof(NodeMode), intValue))
                {
                    nodeMode = (NodeMode)intValue;
                    return true;
                }
 
                return false;
            }
 
            // Try to parse as an enum name (case-insensitive)
            if (Enum.TryParse(value, ignoreCase: true, out NodeMode enumValue) && Enum.IsDefined(typeof(NodeMode), enumValue))
            {
                nodeMode = enumValue;
                return true;
            }
 
            return false;
        }
 
        /// <summary>
        /// Extracts the NodeMode from a command line string using regex pattern matching.
        /// </summary>
        /// <param name="commandLine">The command line to parse. Note that this can't be a span because generated regex don't have a Span Match overload</param>
        /// <returns>The NodeMode if found, otherwise null</returns>
        public static NodeMode? ExtractFromCommandLine(string commandLine)
        {
            if (string.IsNullOrWhiteSpace(commandLine))
            {
                return null;
            }
 
            var match = CommandLineNodeModeRegex.Match(commandLine);
 
            if (!match.Success)
            {
                return null;
            }
 
#if NET
            if (TryParse(match.Groups["nodemode"].ValueSpan, out NodeMode? nodeMode))
#else
            if (TryParse(match.Groups["nodemode"].Value, out NodeMode? nodeMode))
#endif
            {
                return nodeMode;
            }
 
            return null;
        }
 
        private const string CommandLineNodeModePattern = @"/nodemode:(?<nodemode>[a-zA-Z0-9]+)(?:\s|$)";
 
#if NET
        [System.Text.RegularExpressions.GeneratedRegex(CommandLineNodeModePattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase)]
        private static partial System.Text.RegularExpressions.Regex CommandLineNodeModeRegex { get; }
#else
        private static System.Text.RegularExpressions.Regex CommandLineNodeModeRegex { get; } = new(
            CommandLineNodeModePattern, 
            System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Compiled);
#endif
    }
}