File: Utilities\OptionExtensions.cs
Web Access
Project: src\src\sdk\src\Cli\Microsoft.DotNet.Cli.Definitions\Microsoft.DotNet.Cli.Definitions.csproj (Microsoft.DotNet.Cli.Definitions)
// 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.ObjectModel;
using System.CommandLine;
using Microsoft.DotNet.Cli.CommandLine;

namespace Microsoft.DotNet.Cli;

public static class OptionExtensions
{
    public static Option<T> AsHidden<T>(this Option<T> o)
    {
        o.Hidden = true;
        return o;
    }

    public static Option<T> WithDescription<T>(this Option<T> o, string description)
    {
        o.Description = description;
        return o;
    }

    public static Option<T> DisableAllowMultipleArgumentsPerToken<T>(this Option<T> o)
    {
        o.AllowMultipleArgumentsPerToken = false;
        return o;
    }


    /// <summary>
    /// Set up an option to be forwarded as an output path to MSBuild
    /// </summary>
    /// <param name="option">The command line option</param>
    /// <param name="outputPropertyName">The property name for the output path (such as OutputPath or PublishDir)</param>
    /// <param name="surroundWithDoubleQuotes">Whether the path should be surrounded with double quotes.  This may not be necessary but preserves the previous behavior of "dotnet test"</param>
    /// <returns>The option</returns>
    public static Option<string> ForwardAsOutputPath(this Option<string> option, string outputPropertyName, bool surroundWithDoubleQuotes = false)
    {
        return option.SetForwardingFunction((string? o) =>
        {
            if (o is null)
            {
                return [];
            }
            string argVal = CommandDirectoryContext.GetFullPath(o);
            if (!Path.EndsInDirectorySeparator(argVal))
            {
                argVal += Path.DirectorySeparatorChar;
            }
            if (surroundWithDoubleQuotes)
            {
                //  Not sure if this is necessary, but this is what "dotnet test" previously did and so we are
                //  preserving the behavior here after refactoring
                argVal = MSBuildPropertyParser.SurroundWithDoubleQuotes(argVal);
            }
            return [
                $"--property:{outputPropertyName}={argVal}",
                "--property:_CommandLineDefinedOutputPath=true"
            ];
        });
    }

    /// <summary>
    /// Set up an option to be forwarded as an MSBuild property
    /// This will parse the values as MSBuild properties and forward them in the format <c>optionName:key=value</c>.
    /// For example, if the option is named "--property", and the values are "A=B" and "C=D", it will be forwarded as:
    /// <c>--property:A=B --property:C=D</c>.
    /// This is useful for options that can take multiple key-value pairs, such as --property.
    /// </summary>
    public static Option<ReadOnlyDictionary<string, string>?> ForwardAsMSBuildProperty(this Option<ReadOnlyDictionary<string, string>?> option) =>
        option.SetForwardingFunction(propertyDict => ForwardedMSBuildPropertyValues(propertyDict, option.Name));

    private static IEnumerable<string> ForwardedMSBuildPropertyValues(ReadOnlyDictionary<string, string>? properties, string optionName)
    {
        if (properties is null || properties.Count == 0)
        {
            return Enumerable.Empty<string>();
        }

        return properties.Select(kv => $"{optionName}:{kv.Key}={kv.Value}");
    }
}