QueryHelper.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System;
  6  using System.Collections.Generic;
  7  using System.Linq;
  8  using System.Text.RegularExpressions;
  9  
 10  using Microsoft.CmdPal.Ext.Registry.Constants;
 11  
 12  namespace Microsoft.CmdPal.Ext.Registry.Helpers;
 13  
 14  /// <summary>
 15  /// Helper class to easier work with queries
 16  /// </summary>
 17  internal static partial class QueryHelper
 18  {
 19      /// <summary>
 20      /// The character to distinguish if the search query contain multiple parts (typically "\\")
 21      /// </summary>
 22      internal const string QuerySplitCharacter = "\\\\";
 23  
 24      /// <summary>
 25      /// A list that contain short names of all registry base keys
 26      /// </summary>
 27      private static readonly IReadOnlyDictionary<string, string> _shortBaseKeys = new Dictionary<string, string>(6)
 28      {
 29          { Win32.Registry.ClassesRoot.Name, KeyName.ClassRootShort },
 30          { Win32.Registry.CurrentConfig.Name, KeyName.CurrentConfigShort },
 31          { Win32.Registry.CurrentUser.Name, KeyName.CurrentUserShort },
 32          { Win32.Registry.LocalMachine.Name, KeyName.LocalMachineShort },
 33          { Win32.Registry.PerformanceData.Name, KeyName.PerformanceDataShort },
 34          { Win32.Registry.Users.Name, KeyName.UsersShort },
 35      };
 36  
 37      [GeneratedRegex(@"/(?<=^(?:[^""]*""[^""]*"")*[^""]*)(?<!//.+)", RegexOptions.IgnoreCase, "en-US")]
 38      private static partial Regex FrontToBackSlashRegex();
 39  
 40      /// <summary>
 41      /// Sanitize the query to avoid issues with the regex
 42      /// </summary>
 43      /// <param name="query">Query containing front-slash</param>
 44      /// <returns>A string replacing all the front-slashes with back-slashes</returns>
 45      private static string SanitizeQuery(in string query)
 46      {
 47          var sanitizedQuery = FrontToBackSlashRegex().Replace(query, "\\");
 48  
 49          return sanitizedQuery.Replace("\"", string.Empty);
 50      }
 51  
 52      /// <summary>
 53      /// Return the parts of a given query
 54      /// </summary>
 55      /// <param name="query">The query that could contain parts</param>
 56      /// <param name="queryKey">The key part of the query</param>
 57      /// <param name="queryValueName">The value name part of the query</param>
 58      /// <returns><see langword="true"/> when the query search for a key and a value name; otherwise, <see langword="false"/></returns>
 59      internal static bool GetQueryParts(in string query, out string queryKey, out string queryValueName)
 60      {
 61          var sanitizedQuery = SanitizeQuery(query);
 62  
 63          if (!sanitizedQuery.Contains(QuerySplitCharacter, StringComparison.InvariantCultureIgnoreCase))
 64          {
 65              queryKey = sanitizedQuery;
 66              queryValueName = string.Empty;
 67              return false;
 68          }
 69  
 70          var querySplit = sanitizedQuery.Split(QuerySplitCharacter);
 71  
 72          queryKey = querySplit.First();
 73          queryValueName = querySplit.Last();
 74          return true;
 75      }
 76  
 77      /// <summary>
 78      /// Return a registry key with a long base key
 79      /// </summary>
 80      /// <param name="registryKey">A registry key with a short base key</param>
 81      /// <returns>A registry key with a long base key</returns>
 82      internal static string GetKeyWithLongBaseKey(in string registryKey)
 83      {
 84          foreach (var shortName in _shortBaseKeys)
 85          {
 86              if (!registryKey.StartsWith(shortName.Value, StringComparison.InvariantCultureIgnoreCase))
 87              {
 88                  continue;
 89              }
 90  
 91              return registryKey.Replace(shortName.Value, shortName.Key, StringComparison.InvariantCultureIgnoreCase);
 92          }
 93  
 94          return registryKey;
 95      }
 96  
 97      /// <summary>
 98      /// Return a registry key with a short base key (useful to reduce the text length of a registry key)
 99      /// </summary>
100      /// <param name="registryKey">A registry key with a full base key</param>
101      /// <returns>A registry key with a short base key</returns>
102      internal static string GetKeyWithShortBaseKey(in string registryKey)
103      {
104          foreach (var shortName in _shortBaseKeys)
105          {
106              if (!registryKey.StartsWith(shortName.Key, StringComparison.InvariantCultureIgnoreCase))
107              {
108                  continue;
109              }
110  
111              return registryKey.Replace(shortName.Key, shortName.Value, StringComparison.InvariantCultureIgnoreCase);
112          }
113  
114          return registryKey;
115      }
116  }