winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

PackageCommand.cs (6352B)


      1 // -----------------------------------------------------------------------------
      2 // <copyright file="PackageCommand.cs" company="Microsoft Corporation">
      3 //     Copyright (c) Microsoft Corporation. Licensed under the MIT License.
      4 // </copyright>
      5 // -----------------------------------------------------------------------------
      6 
      7 namespace Microsoft.WinGet.Client.Engine.Commands.Common
      8 {
      9     using System;
     10     using System.Collections.Generic;
     11     using System.Management.Automation;
     12     using System.Threading.Tasks;
     13     using Microsoft.Management.Deployment;
     14     using Microsoft.WinGet.Client.Engine.Exceptions;
     15     using Microsoft.WinGet.Client.Engine.Extensions;
     16     using Microsoft.WinGet.Client.Engine.Helpers;
     17     using Microsoft.WinGet.Client.Engine.PSObjects;
     18 
     19     /// <summary>
     20     /// This is the base class for commands which operate on a specific package and version i.e.,
     21     /// the "install", "uninstall", "download", and "upgrade" commands.
     22     /// </summary>
     23     public abstract class PackageCommand : FinderCommand
     24     {
     25         /// <summary>
     26         /// Initializes a new instance of the <see cref="PackageCommand"/> class.
     27         /// </summary>
     28         /// <param name="psCmdlet">PSCmdlet.</param>
     29         internal PackageCommand(PSCmdlet psCmdlet)
     30             : base(psCmdlet)
     31         {
     32         }
     33 
     34         /// <summary>
     35         /// Gets or sets the package to directly install.
     36         /// </summary>
     37         /// <remarks>
     38         /// Must match the name of the <see cref="CatalogPackage" /> field on the <see cref="MatchResult" /> class.
     39         /// </remarks>
     40         protected PSCatalogPackage? CatalogPackage { get; set; } = null;
     41 
     42         /// <summary>
     43         /// Gets or sets the version to install.
     44         /// </summary>
     45         protected string? Version { get; set; }
     46 
     47         /// <summary>
     48         /// Executes a command targeting a specific package version.
     49         /// </summary>
     50         /// <typeparam name="TResult">Type of callback's result.</typeparam>
     51         /// <param name="behavior">The <see cref="CompositeSearchBehavior" /> value.</param>
     52         /// <param name="match">The match option.</param>
     53         /// <param name="callback">The method to call after retrieving the package and version to operate upon.</param>
     54         /// <returns>Result of the callback.</returns>
     55         internal async Task<Tuple<TResult, CatalogPackage>?> GetPackageAndExecuteAsync<TResult>(
     56             CompositeSearchBehavior behavior,
     57             PackageFieldMatchOption match,
     58             Func<CatalogPackage, PackageVersionId?, Task<TResult>> callback)
     59             where TResult : class
     60         {
     61             CatalogPackage package = this.GetCatalogPackage(behavior, match);
     62             PackageVersionId? version = this.GetPackageVersionId(package);
     63             if (this.ShouldProcess(package.ToString(version)))
     64             {
     65                 var result = await callback(package, version);
     66                 return new Tuple<TResult, CatalogPackage>(result, package);
     67             }
     68 
     69             return null;
     70         }
     71 
     72         /// <summary>
     73         /// Sets the find package options for a query input that is looking for a specific package.
     74         /// DO NOT pass PackageFieldMatchOption WinRT enum type in this method.
     75         /// That will cause the type to attempt to be loaded in the construction
     76         /// of this method and throw a different exception for Windows PowerShell.
     77         /// </summary>
     78         /// <param name="options">The options object.</param>
     79         /// <param name="match">The match type.</param>
     80         /// <param name="value">The query value.</param>
     81         internal override void SetQueryInFindPackagesOptions(
     82             ref FindPackagesOptions options,
     83             string match,
     84             string? value)
     85         {
     86             var matchOption = PSEnumHelpers.ToPackageFieldMatchOption(match);
     87             foreach (PackageMatchField field in new PackageMatchField[] { PackageMatchField.Id, PackageMatchField.Name, PackageMatchField.Moniker })
     88             {
     89                 var selector = ManagementDeploymentFactory.Instance.CreatePackageMatchFilter();
     90                 selector.Field = field;
     91                 selector.Value = value ?? string.Empty;
     92                 selector.Option = matchOption;
     93                 options.Selectors.Add(selector);
     94             }
     95         }
     96 
     97         private CatalogPackage GetCatalogPackage(CompositeSearchBehavior behavior, PackageFieldMatchOption match)
     98         {
     99             if (this.CatalogPackage != null)
    100             {
    101                 // The package was already provided via a parameter or the pipeline.
    102                 return this.CatalogPackage.CatalogPackageCOM;
    103             }
    104             else
    105             {
    106                 IReadOnlyList<MatchResult> results = this.FindPackages(behavior, 0, match);
    107                 if (results.Count == 1)
    108                 {
    109                     // Exactly one package matched, so we can just return it.
    110                     return results[0].CatalogPackage;
    111                 }
    112                 else if (results.Count == 0)
    113                 {
    114                     // No packages matched, we need to throw an error.
    115                     throw new NoPackageFoundException();
    116                 }
    117                 else
    118                 {
    119                     // Too many packages matched! The user needs to refine their input.
    120                     throw new VagueCriteriaException(results);
    121                 }
    122             }
    123         }
    124 
    125         private PackageVersionId? GetPackageVersionId(CatalogPackage package)
    126         {
    127             if (this.Version != null)
    128             {
    129                 for (var i = 0; i < package.AvailableVersions.Count; i++)
    130                 {
    131                     PackageVersionInfo versionInfo = package.GetPackageVersionInfo(package.AvailableVersions[i]);
    132 
    133                     if (versionInfo != null && versionInfo.CompareToVersion(this.Version) == CompareResult.Equal)
    134                     {
    135                         return package.AvailableVersions[i];
    136                     }
    137                 }
    138 
    139                 throw new InvalidVersionException(this.Version);
    140             }
    141             else
    142             {
    143                 return null;
    144             }
    145         }
    146     }
    147 }