winget-cli

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

FinderCommand.cs (8385B)


      1 // -----------------------------------------------------------------------------
      2 // <copyright file="FinderCommand.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.Linq;
     12     using System.Management.Automation;
     13     using System.Reflection;
     14     using Microsoft.Management.Deployment;
     15     using Microsoft.WinGet.Client.Engine.Attributes;
     16     using Microsoft.WinGet.Client.Engine.Exceptions;
     17     using Microsoft.WinGet.Client.Engine.Helpers;
     18 
     19     /// <summary>
     20     /// This is the base class for all commands that might need to search for a package. It contains an initial
     21     /// set of parameters that corresponds to the intersection of i.e., the "install" and "search" commands.
     22     /// </summary>
     23     public abstract class FinderCommand : ManagementDeploymentCommand
     24     {
     25         /// <summary>
     26         /// Initializes a new instance of the <see cref="FinderCommand"/> class.
     27         /// </summary>
     28         /// <param name="psCmdlet">PSCmdlet.</param>
     29         internal FinderCommand(PSCmdlet psCmdlet)
     30             : base(psCmdlet)
     31         {
     32         }
     33 
     34         /// <summary>
     35         /// Gets or sets the field that is matched against the identifier of a package.
     36         /// </summary>
     37         [Filter(Field = PackageMatchField.Id)]
     38         protected string? Id { get; set; }
     39 
     40         /// <summary>
     41         /// Gets or sets the field that is matched against the name of a package.
     42         /// </summary>
     43         [Filter(Field = PackageMatchField.Name)]
     44         protected string? Name { get; set; }
     45 
     46         /// <summary>
     47         /// Gets or sets the field that is matched against the moniker of a package.
     48         /// </summary>
     49         [Filter(Field = PackageMatchField.Moniker)]
     50         protected string? Moniker { get; set; }
     51 
     52         /// <summary>
     53         /// Gets or sets the name of the source to search for packages. If null, then all sources are searched.
     54         /// </summary>
     55         protected string? Source { get; set; }
     56 
     57         /// <summary>
     58         /// Gets or sets how to match against package fields.
     59         /// </summary>
     60 #pragma warning disable SA1011 // Closing square brackets should be spaced correctly
     61         protected string[]? Query { get; set; }
     62 #pragma warning restore SA1011 // Closing square brackets should be spaced correctly
     63 
     64         private string? QueryAsJoinedString
     65         {
     66             get
     67             {
     68                 return this.Query is null
     69                     ? null
     70                     : string.Join(" ", this.Query);
     71             }
     72         }
     73 
     74         /// <summary>
     75         /// Searches for packages based on the configured parameters.
     76         /// </summary>
     77         /// <param name="behavior">The <see cref="CompositeSearchBehavior" /> value.</param>
     78         /// <param name="limit">The limit on the number of matches returned.</param>
     79         /// <param name="match">The match option.</param>
     80         /// <returns>A list of <see cref="MatchResult" /> objects.</returns>
     81         internal IReadOnlyList<MatchResult> FindPackages(
     82             CompositeSearchBehavior behavior,
     83             uint limit,
     84             PackageFieldMatchOption match)
     85         {
     86             PackageCatalog catalog = this.GetPackageCatalog(behavior);
     87             FindPackagesOptions options = this.GetFindPackagesOptions(limit, match);
     88             return this.GetMatchResults(catalog, options);
     89         }
     90 
     91         /// <summary>
     92         /// Sets the find package options for a query input.
     93         /// DO NOT pass PackageFieldMatchOption WinRT enum type in this method.
     94         /// That will cause the type to attempt to be loaded in the construction
     95         /// of this method and throw a different exception for Windows PowerShell.
     96         /// </summary>
     97         /// <param name="options">The options object.</param>
     98         /// <param name="match">The match type as string.</param>
     99         /// <param name="value">The query value.</param>
    100         internal virtual void SetQueryInFindPackagesOptions(
    101             ref FindPackagesOptions options,
    102             string match,
    103             string? value)
    104         {
    105             var selector = ManagementDeploymentFactory.Instance.CreatePackageMatchFilter();
    106             selector.Field = PackageMatchField.CatalogDefault;
    107             selector.Value = value ?? string.Empty;
    108             selector.Option = PSEnumHelpers.ToPackageFieldMatchOption(match);
    109             options.Selectors.Add(selector);
    110         }
    111 
    112         private void AddFilterToFindPackagesOptionsIfNotNull(
    113             ref FindPackagesOptions options,
    114             PackageMatchField field,
    115             PackageFieldMatchOption match,
    116             string? value)
    117         {
    118             if (value != null)
    119             {
    120                 var filter = ManagementDeploymentFactory.Instance.CreatePackageMatchFilter();
    121                 filter.Field = field;
    122                 filter.Value = value;
    123                 filter.Option = match;
    124                 options.Filters.Add(filter);
    125             }
    126         }
    127 
    128         private IReadOnlyList<MatchResult> GetMatchResults(
    129             PackageCatalog catalog,
    130             FindPackagesOptions options)
    131         {
    132             FindPackagesResult result = catalog.FindPackages(options);
    133             if (result.Status == FindPackagesResultStatus.Ok)
    134             {
    135                 return result.Matches;
    136             }
    137             else
    138             {
    139                 throw new FindPackagesException(result.Status);
    140             }
    141         }
    142 
    143         private PackageCatalog GetPackageCatalog(CompositeSearchBehavior behavior)
    144         {
    145             PackageCatalogReference reference = this.GetPackageCatalogReference(behavior);
    146             ConnectResult result = reference.Connect();
    147             if (result.Status == ConnectResultStatus.Ok)
    148             {
    149                 return result.PackageCatalog;
    150             }
    151             else
    152             {
    153                 throw new CatalogConnectException(result.ExtendedErrorCode);
    154             }
    155         }
    156 
    157         private PackageCatalogReference GetPackageCatalogReference(CompositeSearchBehavior behavior)
    158         {
    159             CreateCompositePackageCatalogOptions options = ManagementDeploymentFactory.Instance.CreateCreateCompositePackageCatalogOptions();
    160             IReadOnlyList<PackageCatalogReference> references = this.GetPackageCatalogReferences(this.Source);
    161             for (var i = 0; i < references.Count; i++)
    162             {
    163                 options.Catalogs.Add(references[i]);
    164             }
    165 
    166             options.CompositeSearchBehavior = behavior;
    167             return PackageManagerWrapper.Instance.CreateCompositePackageCatalog(options);
    168         }
    169 
    170         private FindPackagesOptions GetFindPackagesOptions(uint limit, PackageFieldMatchOption match)
    171         {
    172             var options = ManagementDeploymentFactory.Instance.CreateFindPackagesOptions();
    173             this.SetQueryInFindPackagesOptions(ref options, match.ToString(), this.QueryAsJoinedString);
    174             this.AddAttributedFiltersToFindPackagesOptions(ref options, match);
    175             options.ResultLimit = limit;
    176             return options;
    177         }
    178 
    179         private void AddAttributedFiltersToFindPackagesOptions(
    180             ref FindPackagesOptions options,
    181             PackageFieldMatchOption match)
    182         {
    183             IEnumerable<PropertyInfo> properties = this.GetType()
    184                 .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
    185                 .Where(property => Attribute.IsDefined(property, typeof(FilterAttribute)));
    186 
    187             foreach (PropertyInfo info in properties)
    188             {
    189                 if (info.GetCustomAttribute(typeof(FilterAttribute), true) is FilterAttribute attribute)
    190                 {
    191                     PackageMatchField field = attribute.Field;
    192                     string? value = info.GetValue(this, null) as string;
    193                     this.AddFilterToFindPackagesOptionsIfNotNull(ref options, field, match, value);
    194                 }
    195             }
    196         }
    197     }
    198 }