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 }