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 }