File: Commands\Tool\Restore\ToolPackageRestorer.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.
 
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.ToolManifest;
using Microsoft.DotNet.Cli.ToolPackage;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Frameworks;
using NuGet.Versioning;
using static Microsoft.DotNet.Cli.Commands.Tool.Restore.ToolRestoreCommand;
 
namespace Microsoft.DotNet.Cli.Commands.Tool.Restore;
 
internal class ToolPackageRestorer
{
    private readonly IToolPackageDownloader _toolPackageDownloader;
    private readonly string[] _additionalSources;
    private readonly string[] _overrideSources;
    private readonly VerbosityOptions _verbosity;
    private readonly RestoreActionConfig _restoreActionConfig;
 
    private readonly ILocalToolsResolverCache _localToolsResolverCache;
    private readonly IFileSystem _fileSystem;
 
 
 
    public ToolPackageRestorer(IToolPackageDownloader toolPackageDownloader,
                               string[] additionalSources,
                               string[] overrideSources,
                               VerbosityOptions verbosity,
                                RestoreActionConfig restoreActionConfig,
                               ILocalToolsResolverCache? localToolsResolverCache = null,
                               IFileSystem? fileSystem = null)
    {
        _toolPackageDownloader = toolPackageDownloader;
        _additionalSources = additionalSources;
        _overrideSources = overrideSources;
        _verbosity = verbosity;
        _restoreActionConfig = restoreActionConfig;
 
        _localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
        _fileSystem = fileSystem ?? new FileSystemWrapper();
    }
 
    public ToolRestoreResult InstallPackage(
        ToolManifestPackage package,
        FilePath? configFile)
    {
        string targetFramework = BundledTargetFramework.GetTargetFrameworkMoniker();
 
        if (PackageHasBeenRestored(package, targetFramework))
        {
            return ToolRestoreResult.Success(
                saveToCache: null,
                message: string.Format(
                    CliCommandStrings.RestoreSuccessful, package.PackageId,
                    package.Version.ToNormalizedString(), string.Join(", ", package.CommandNames)));
        }
 
        try
        {
            IToolPackage toolPackage =
                _toolPackageDownloader.InstallPackage(
                    new PackageLocation(
                        nugetConfig: configFile,
                        additionalFeeds: _additionalSources,
                        sourceFeedOverrides: _overrideSources,
                        rootConfigDirectory: package.FirstEffectDirectory),
                    package.PackageId,
                    verbosity: _verbosity,
                    ToVersionRangeWithOnlyOneVersion(package.Version),
                    targetFramework,
                    restoreActionConfig: _restoreActionConfig
                    );
 
            if (!ManifestCommandMatchesActualInPackage(package.CommandNames, [toolPackage.Command]))
            {
                return ToolRestoreResult.Failure(
                    string.Format(CliCommandStrings.CommandsMismatch,
                        JoinBySpaceWithQuote(package.CommandNames.Select(c => c.Value.ToString())),
                        package.PackageId,
                        toolPackage.Command.Name));
            }
 
            return ToolRestoreResult.Success(
                saveToCache:
                    (new RestoredCommandIdentifier(
                        toolPackage.Id,
                        toolPackage.Version,
                        NuGetFramework.Parse(targetFramework),
                        Constants.AnyRid,
                        toolPackage.Command.Name),
                    toolPackage.Command),
                message: string.Format(
                    CliCommandStrings.RestoreSuccessful,
                    package.PackageId,
                    package.Version.ToNormalizedString(),
                    string.Join(" ", package.CommandNames)));
        }
        catch (ToolPackageException e)
        {
            return ToolRestoreResult.Failure(package.PackageId, e);
        }
    }
 
    private static bool ManifestCommandMatchesActualInPackage(
                            ToolCommandName[] commandsFromManifest,
                            IReadOnlyList<ToolCommand> toolPackageCommands)
    {
        ToolCommandName[] commandsFromPackage = [.. toolPackageCommands.Select(t => t.Name)];
return !commandsFromManifest.Any(cmd => !commandsFromPackage.Contains(cmd)) && !commandsFromPackage.Any(cmd => !commandsFromManifest.Contains(cmd));
    }
 
    public bool PackageHasBeenRestored(
        ToolManifestPackage package,
        string targetFramework)
    {
        var sampleRestoredCommandIdentifierOfThePackage = new RestoredCommandIdentifier(
            package.PackageId,
            package.Version,
            NuGetFramework.Parse(targetFramework),
            Constants.AnyRid,
            package.CommandNames.First());
 
        return _localToolsResolverCache.TryLoad(
                   sampleRestoredCommandIdentifierOfThePackage,
                   out var toolCommand)
               && _fileSystem.File.Exists(toolCommand.Executable.Value);
    }
 
    private static string JoinBySpaceWithQuote(IEnumerable<object> objects)
    {
        return string.Join(" ", objects.Select(o => $"\"{o.ToString()}\""));
    }
 
    private static VersionRange ToVersionRangeWithOnlyOneVersion(NuGetVersion version)
    {
        return new VersionRange(
            version,
            includeMinVersion: true,
            maxVersion: version,
            includeMaxVersion: true);
    }
}