File: CommandFactory\CommandResolution\LocalToolsCommandResolver.cs
Web Access
Project: src\src\sdk\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.

using Microsoft.DotNet.Cli.Commands.Tool;
using Microsoft.DotNet.Cli.ToolManifest;
using Microsoft.DotNet.Cli.ToolPackage;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Frameworks;

namespace Microsoft.DotNet.Cli.CommandFactory.CommandResolution;

internal class LocalToolsCommandResolver(
    ToolManifestFinder? toolManifest = null,
    ILocalToolsResolverCache? localToolsResolverCache = null,
    IFileSystem? fileSystem = null,
    string? currentWorkingDirectory = null) : ICommandResolver
{
    private readonly ToolManifestFinder _toolManifest = toolManifest ?? new ToolManifestFinder(new DirectoryPath(currentWorkingDirectory ?? Directory.GetCurrentDirectory()));
    private readonly ILocalToolsResolverCache _localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
    private readonly IFileSystem _fileSystem = fileSystem ?? new FileSystemWrapper();
    private const string LeadingDotnetPrefix = "dotnet-";

    public CommandSpec? ResolveStrict(CommandResolverArguments arguments, bool allowRollForward = false)
    {
        if (arguments == null || string.IsNullOrWhiteSpace(arguments.CommandName))
        {
            return null;
        }

        if (!arguments.CommandName.StartsWith(LeadingDotnetPrefix, StringComparison.OrdinalIgnoreCase))
        {
            return null;
        }

        var resolveResult = GetPackageCommandSpecUsingMuxer(arguments,
            new ToolCommandName(arguments.CommandName.Substring(LeadingDotnetPrefix.Length)), allowRollForward);

        return resolveResult;
    }

    public CommandSpec? Resolve(CommandResolverArguments arguments)
    {
        if (arguments == null || string.IsNullOrWhiteSpace(arguments.CommandName))
        {
            return null;
        }

        if (!arguments.CommandName.StartsWith(LeadingDotnetPrefix, StringComparison.OrdinalIgnoreCase) ||
            string.IsNullOrWhiteSpace(arguments.CommandName.Substring(LeadingDotnetPrefix.Length)))
        {
            return null;
        }

        // Try resolving without prefix first
        var result = GetPackageCommandSpecUsingMuxer(
            arguments,
            new ToolCommandName(arguments.CommandName.Substring(LeadingDotnetPrefix.Length)));

        if (result != null)
        {
            return result;
        }

        // Fallback to resolving with the prefix
        return GetPackageCommandSpecUsingMuxer(arguments, new ToolCommandName(arguments.CommandName));
    }

    private CommandSpec? GetPackageCommandSpecUsingMuxer(CommandResolverArguments arguments,
        ToolCommandName toolCommandName, bool allowRollForward = false)
    {
        if (!_toolManifest.TryFind(toolCommandName, out var toolManifestPackage))
        {
            return null;
        }

        if (_localToolsResolverCache.TryLoad(
            new RestoredCommandIdentifier(
                toolManifestPackage.PackageId,
                toolManifestPackage.Version,
                NuGetFramework.Parse(BundledTargetFramework.GetTargetFrameworkMoniker()),
                Constants.AnyRid,
                toolCommandName),
            out var toolCommand))
        {
            if (!_fileSystem.File.Exists(toolCommand.Executable.Value))
            {
                throw new GracefulException(string.Format(CliStrings.NeedRunToolRestore,
                    toolCommandName.ToString()));
            }

            return ToolCommandSpecCreator.CreateToolCommandSpec(toolCommand.Name.Value, toolCommand.Executable.Value, toolCommand.Runner,
                toolManifestPackage.RollForward || allowRollForward, arguments.CommandArguments ?? []);
        }
        else
        {
            throw new GracefulException(string.Format(CliStrings.NeedRunToolRestore,
                    toolCommandName.ToString()));
        }
    }
}