File: CommandLineBuilder.cs
Web Access
Project: src\src\Aspire.Hosting.Dapr\Aspire.Hosting.Dapr.csproj (Aspire.Hosting.Dapr)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using System.Text;
 
namespace Aspire.Hosting.Dapr;
 
internal delegate IEnumerable<object> CommandLineArgBuilder();
 
internal sealed record CommandLine(string FileName, IEnumerable<object> Arguments)
{
    public string ArgumentString
    {
        get
        {
            StringBuilder builder = new();
 
            var args = this.Arguments.ToList();
 
            for (int i = 0; i < args.Count; i++)
            {
                var arg = args[i];
 
                if (arg is not null)
                {
                    if (i > 0)
                    {
                        builder.Append(' ');
                    }
 
                    builder.Append(arg);
                }
            }
 
            return builder.ToString();
        }
    }
}
 
internal static class CommandLineBuilder
{
    public static CommandLine Create(string fileName, params CommandLineArgBuilder[] argBuilders)
    {
        return new CommandLine(fileName, argBuilders.SelectMany(builder => builder()));
    }
}
 
internal static class CommandLineArgs
{
    public static CommandLineArgBuilder Args(params string[] args)
    {
        return Args((IEnumerable<string>)args);
    }
 
    public static CommandLineArgBuilder Args(IEnumerable<string>? args)
    {
        return () => (args ?? Enumerable.Empty<string>());
    }
 
    public static CommandLineArgBuilder Command(params string[] commands)
    {
        return () => commands;
    }
 
    public static CommandLineArgBuilder Command(CommandLine commandLine)
    {
        return Command(new[] { commandLine.FileName, commandLine.ArgumentString });
    }
 
    public static CommandLineArgBuilder Flag(string name)
    {
        return Flag(name, true);
    }
 
    public static CommandLineArgBuilder Flag(string name, bool? value)
    {
        return () => value == true ? new[] { name } : Enumerable.Empty<string>();
    }
 
    public static CommandLineArgBuilder NamedArg<T>(string name, T value, bool assignValue = false) where T : struct
    {
        return () =>
        {
            string? stringValue = Convert.ToString(value, CultureInfo.InvariantCulture);
 
            return stringValue is not null
                ? NamedStringArg(name, stringValue, assignValue)()
                : Enumerable.Empty<string>();
        };
    }
 
    public static CommandLineArgBuilder NamedArg<T>(string name, T? value, bool assignValue = false) where T : struct
    {
        return () =>
            value.HasValue
                ? NamedArg(name, value.Value, assignValue)()
                : Enumerable.Empty<string>();
    }
 
    public static CommandLineArgBuilder NamedArg(string name, string? value, bool assignValue = false)
    {
        return () =>
        {
            return value is not null
                ? NamedStringArg(name, value, assignValue)()
                : Enumerable.Empty<string>();
        };
    }
 
    public static CommandLineArgBuilder ModelNamedArg<T>(string name, T value, bool assignValue = false) where T : struct
    {
        return () =>
        {
            string? stringValue = Convert.ToString(value, CultureInfo.InvariantCulture);
 
            return stringValue is not null
                ? ModelNamedStringArg(name, stringValue, assignValue)()
                : Enumerable.Empty<string>();
        };
    }
 
    public static CommandLineArgBuilder ModelNamedArg<T>(string name, T? value, bool assignValue = false) where T : struct
    {
        return () =>
            value.HasValue
                ? ModelNamedArg(name, value.Value, assignValue)()
                : Enumerable.Empty<string>();
    }
 
    public static CommandLineArgBuilder ModelNamedArg(string name, string? value, bool assignValue = false)
    {
        return () =>
        {
            return value is not null
                ? ModelNamedStringArg(name, value, assignValue)()
                : Enumerable.Empty<string>();
        };
    }
 
    public static CommandLineArgBuilder NamedArg(string name, IEnumerable<string>? values, bool assignValue = false)
    {
        return () =>
        {
            return (values ?? Enumerable.Empty<string>()).SelectMany(value => NamedArg(name, value, assignValue)());
        };
    }
 
    public static CommandLineArgBuilder ModelNamedArg(string name, IEnumerable<string>? values, bool assignValue = false)
    {
        return () =>
        {
            return (values ?? Enumerable.Empty<string>()).SelectMany(value => ModelNamedArg(name, value, assignValue)());
        };
    }
 
    private static readonly char[] s_reservedChars = new[] { ' ', '&', '|', '(', ')', '<', '>', '^' };
 
    private static CommandLineArgBuilder NamedStringArg(string name, string value, bool assignValue)
    {
        bool hasReservedChars = value.Any(c => s_reservedChars.Contains(c)) == true;
 
        return () =>
        {
            return assignValue
                ? new[] { $"\"{name}={value}\"" }
                : new[] { name, hasReservedChars ? $"\"{value}\"" : value };
        };
    }
 
    private static CommandLineArgBuilder ModelNamedStringArg(string name, string value, bool assignValue)
    {
        return () =>
        {
            return assignValue
                ? new[] { $"{name}={value}" }
                : new[] { name, value };
        };
    }
 
    public static CommandLineArgBuilder ModelNamedObjectArg(string name, object value)
    {
        return () =>
        {
            return [name, value];
        };
    }
 
    public static CommandLineArgBuilder ModelNamedObjectArg<T>(string name, object value, bool assignValue = false) where T : struct
    {
        return () =>
        {
            string? stringValue = Convert.ToString(value, CultureInfo.InvariantCulture);
 
            return stringValue is not null
                ? ModelNamedStringArg(name, stringValue, assignValue)()
                : Enumerable.Empty<string>();
        };
    }
 
    public static CommandLineArgBuilder PostOptionsArgs(params CommandLineArgBuilder[] args)
    {
        return PostOptionsArgs(null, args);
    }
 
    public static CommandLineArgBuilder PostOptionsArgs(string? separator, params CommandLineArgBuilder[] args)
    {
        return PostOptionsArgs(separator, (IEnumerable<CommandLineArgBuilder>)args);
    }
 
    public static CommandLineArgBuilder PostOptionsArgs(string? separator, IEnumerable<CommandLineArgBuilder> args)
    {
        IEnumerable<object> GeneratePostOptionsArgs()
        {
            bool postOptions = false;
 
            foreach (var arg in args.SelectMany(builder => builder()))
            {
                if (!postOptions)
                {
                    postOptions = true;
 
                    yield return separator ?? "--";
                }
 
                yield return arg;
            }
        }
 
        return GeneratePostOptionsArgs;
    }
}