File: Commands\Workload\WorkloadCommandParser.cs
Web Access
Project: ..\..\..\src\Cli\dotnet\dotnet.csproj (dotnet)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable disable
 
using System.CommandLine;
using System.CommandLine.Invocation;
using Microsoft.DotNet.Cli.Commands.Workload.Clean;
using Microsoft.DotNet.Cli.Commands.Workload.Config;
using Microsoft.DotNet.Cli.Commands.Workload.Elevate;
using Microsoft.DotNet.Cli.Commands.Workload.History;
using Microsoft.DotNet.Cli.Commands.Workload.Install;
using Microsoft.DotNet.Cli.Commands.Workload.List;
using Microsoft.DotNet.Cli.Commands.Workload.Repair;
using Microsoft.DotNet.Cli.Commands.Workload.Restore;
using Microsoft.DotNet.Cli.Commands.Workload.Search;
using Microsoft.DotNet.Cli.Commands.Workload.Uninstall;
using Microsoft.DotNet.Cli.Commands.Workload.Update;
using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.NET.Sdk.WorkloadManifestReader;
using Microsoft.TemplateEngine.Cli.Commands;
using IReporter = Microsoft.DotNet.Cli.Utils.IReporter;
using Command = System.CommandLine.Command;
 
namespace Microsoft.DotNet.Cli.Commands.Workload;
 
internal static class WorkloadCommandParser
{
    public static readonly string DocsLink = "https://aka.ms/dotnet-workload";
    public static readonly Option<bool> InfoOption = new("--info")
    {
        Description = CliCommandStrings.WorkloadInfoDescription,
        Arity = ArgumentArity.Zero,
        Action = new ShowWorkloadsInfoAction()
    };
 
    public static readonly Option<bool> VersionOption = new("--version")
    {
        Description = CliCommandStrings.WorkloadVersionDescription,
        Arity = ArgumentArity.Zero,
        Action = new ShowWorkloadsVersionOption()
    };
 
    public static Command GetCommand()
    {
        return Command;
    }
 
    private static readonly Command Command = ConstructCommand();
 
    internal static string GetWorkloadsVersion(WorkloadInfoHelper workloadInfoHelper = null)
    {
        workloadInfoHelper ??= new WorkloadInfoHelper(false);
 
        var versionInfo = workloadInfoHelper.ManifestProvider.GetWorkloadVersion();
 
        // The explicit space here is intentional, as it's easy to miss in localization and crucial for parsing
        return versionInfo.Version + (versionInfo.IsInstalled ? string.Empty : ' ' + CliCommandStrings.WorkloadVersionNotInstalledShort);
    }
 
    internal static void ShowWorkloadsInfo(ParseResult parseResult = null, WorkloadInfoHelper workloadInfoHelper = null, IReporter reporter = null, string dotnetDir = null, bool showVersion = true)
    {
        workloadInfoHelper ??= new WorkloadInfoHelper(parseResult != null ? parseResult.HasOption(SharedOptions.InteractiveOption) : false);
        reporter ??= Reporter.Output;
        var versionInfo = workloadInfoHelper.ManifestProvider.GetWorkloadVersion();
 
        void WriteUpdateModeAndAnyError(string indent = "")
        {
            var useWorkloadSets = InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(workloadInfoHelper._currentSdkFeatureBand, workloadInfoHelper.UserLocalPath), "default.json")).ShouldUseWorkloadSets();
            var configurationMessage = useWorkloadSets
                ? CliCommandStrings.WorkloadManifestInstallationConfigurationWorkloadSets
                : CliCommandStrings.WorkloadManifestInstallationConfigurationLooseManifests;
            reporter.WriteLine(indent + configurationMessage);
 
            if (!versionInfo.IsInstalled)
            {
                reporter.WriteLine(indent + string.Format(CliCommandStrings.WorkloadSetFromGlobalJsonNotInstalled, versionInfo.Version, versionInfo.GlobalJsonPath));
            }
            else if (versionInfo.WorkloadSetsEnabledWithoutWorkloadSet)
            {
                reporter.WriteLine(indent + CliCommandStrings.ShouldInstallAWorkloadSet);
            }
        }
 
        if (showVersion)
        {
            reporter.WriteLine($" Workload version: {GetWorkloadsVersion()}");
 
            WriteUpdateModeAndAnyError(indent: " ");
            reporter.WriteLine();
        }
 
        if (versionInfo.IsInstalled)
        {
            IEnumerable<WorkloadId> installedList = workloadInfoHelper.InstalledSdkWorkloadIds;
            InstalledWorkloadsCollection installedWorkloads = workloadInfoHelper.AddInstalledVsWorkloads(installedList);
            string dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath);
 
            if (installedWorkloads.Count == 0)
            {
                reporter.WriteLine(CliCommandStrings.NoWorkloadsInstalledInfoWarning);
            }
            else
            {
                var manifestInfoDict = workloadInfoHelper.WorkloadResolver.GetInstalledManifests().ToDictionary(info => info.Id, StringComparer.OrdinalIgnoreCase);
 
                foreach (var workload in installedWorkloads.AsEnumerable())
                {
                    var workloadManifest = workloadInfoHelper.WorkloadResolver.GetManifestFromWorkload(new WorkloadId(workload.Key));
                    var workloadFeatureBand = manifestInfoDict[workloadManifest.Id].ManifestFeatureBand;
 
                    const int align = 10;
                    const string separator = "   ";
 
                    reporter.WriteLine($" [{workload.Key}]");
 
                    reporter.Write($"{separator}{CliCommandStrings.WorkloadSourceColumn}:");
                    reporter.WriteLine($" {workload.Value,align}");
 
                    reporter.Write($"{separator}{CliCommandStrings.WorkloadManifestVersionColumn}:");
                    reporter.WriteLine($"    {workloadManifest.Version + '/' + workloadFeatureBand,align}");
 
                    reporter.Write($"{separator}{CliCommandStrings.WorkloadManifestPathColumn}:");
                    reporter.WriteLine($"       {workloadManifest.ManifestPath,align}");
 
                    reporter.Write($"{separator}{CliCommandStrings.WorkloadInstallTypeColumn}:");
                    reporter.WriteLine($"       {WorkloadInstallType.GetWorkloadInstallType(new SdkFeatureBand(Product.Version), dotnetPath).ToString(),align}"
                    );
                    reporter.WriteLine("");
                }
            }
        }
 
        if (!showVersion)
        {
            WriteUpdateModeAndAnyError();
        }
    }
 
    private static int ProcessArgs(ParseResult parseResult) => parseResult.HandleMissingCommand();
 
    private static Command ConstructCommand()
    {
        DocumentedCommand command = new("workload", DocsLink, CliCommandStrings.WorkloadCommandDescription);
 
        command.Subcommands.Add(WorkloadInstallCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadUpdateCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadListCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadSearchCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadUninstallCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadRepairCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadRestoreCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadCleanCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadElevateCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadConfigCommandParser.GetCommand());
        command.Subcommands.Add(WorkloadHistoryCommandParser.GetCommand());
 
        command.Options.Add(InfoOption);
        command.Options.Add(VersionOption);
 
        command.Validators.Add(commandResult =>
        {
            if (commandResult.HasOption(InfoOption) && commandResult.HasOption(VersionOption) && !commandResult.Children.Any(child => child is System.CommandLine.Parsing.CommandResult))
            {
                commandResult.AddError(CliStrings.RequiredCommandNotPassed);
            }
        });
 
        command.SetAction(ProcessArgs);
 
        return command;
    }
 
    private class ShowWorkloadsInfoAction : SynchronousCommandLineAction
    {
        public override bool Terminating => true;
 
        public override int Invoke(ParseResult parseResult)
        {
            ShowWorkloadsInfo(parseResult);
            Reporter.Output.WriteLine(string.Empty);
            return 0;
        }
    }
 
    private class ShowWorkloadsVersionOption : SynchronousCommandLineAction
    {
        public override bool Terminating => true;
 
        public override int Invoke(ParseResult parseResult)
        {
            Reporter.Output.WriteLine(GetWorkloadsVersion());
            Reporter.Output.WriteLine(string.Empty);
            return 0;
        }
    }
}