AppInstallerStrings.h (12660B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #pragma once 4 #include <filesystem> 5 #include <ostream> 6 #include <string> 7 #include <string_view> 8 #include <vector> 9 #include <winget/LocIndependent.h> 10 11 namespace AppInstaller::Utility 12 { 13 // Converts the given UTF16 string to UTF8 14 std::string ConvertToUTF8(std::wstring_view input); 15 16 // Converts the given UTF8 string to UTF16 17 std::wstring ConvertToUTF16(std::string_view input, UINT codePage = CP_UTF8); 18 19 // Tries to convert the given UTF8 string to UTF16 20 std::optional<std::wstring> TryConvertToUTF16(std::string_view input, UINT codePage = CP_UTF8); 21 22 // Converts the given UTF8 string to UTF32 23 std::u32string ConvertToUTF32(std::string_view input); 24 25 // Normalizes a UTF8 string to the given form. 26 std::string Normalize(std::string_view input, NORM_FORM form = NORM_FORM::NormalizationKC); 27 28 // Normalizes a UTF16 string to the given form. 29 std::wstring Normalize(std::wstring_view input, NORM_FORM form = NORM_FORM::NormalizationKC); 30 31 // Replaces any embedded null character found within the string. 32 void ReplaceEmbeddedNullCharacters(std::string& s, char c = ' '); 33 34 // Type to hold and force a normalized UTF8 string. 35 // Also enables further normalization by replacing embedded null characters with spaces. 36 template <NORM_FORM Form = NORM_FORM::NormalizationKC, bool ConvertEmbeddedNullToSpace = false> 37 struct NormalizedUTF8 : public std::string 38 { 39 NormalizedUTF8() = default; 40 41 template <size_t Size> 42 NormalizedUTF8(const char(&s)[Size]) { AssignValue(std::string_view{ s, (s[Size - 1] == '\0' ? Size - 1 : Size) }); } 43 44 NormalizedUTF8(std::string_view sv) { AssignValue(sv); } 45 46 NormalizedUTF8(std::string& s) { AssignValue(s); } 47 NormalizedUTF8(const std::string& s) { AssignValue(s); } 48 NormalizedUTF8(std::string&& s) { AssignValue(s); } 49 50 NormalizedUTF8(std::wstring_view sv) { AssignValue(sv); } 51 52 NormalizedUTF8(const NormalizedUTF8& other) = default; 53 NormalizedUTF8& operator=(const NormalizedUTF8& other) = default; 54 55 NormalizedUTF8(NormalizedUTF8&& other) = default; 56 NormalizedUTF8& operator=(NormalizedUTF8&& other) = default; 57 58 template <size_t Size> 59 NormalizedUTF8& operator=(const char(&s)[Size]) 60 { 61 AssignValue(std::string_view{ s, (s[Size - 1] == '\0' ? Size - 1 : Size) }); 62 return *this; 63 } 64 65 NormalizedUTF8& operator=(std::string_view sv) 66 { 67 AssignValue(sv); 68 return *this; 69 } 70 71 NormalizedUTF8& operator=(const std::string& s) 72 { 73 AssignValue(s); 74 return *this; 75 } 76 77 NormalizedUTF8& operator=(std::string&& s) 78 { 79 AssignValue(s); 80 return *this; 81 } 82 83 private: 84 void AssignValue(std::string_view sv) 85 { 86 assign(Normalize(sv, Form)); 87 88 if constexpr (ConvertEmbeddedNullToSpace) 89 { 90 ReplaceEmbeddedNullCharacters(*this); 91 } 92 } 93 94 void AssignValue(std::wstring_view sv) 95 { 96 assign(ConvertToUTF8(Normalize(sv, Form))); 97 98 if constexpr (ConvertEmbeddedNullToSpace) 99 { 100 ReplaceEmbeddedNullCharacters(*this); 101 } 102 } 103 }; 104 105 using NormalizedString = NormalizedUTF8<NORM_FORM::NormalizationKC, true>; 106 107 // Compares the two UTF8 strings in a case-insensitive manner. 108 // Use this if one of the values is a known value, and thus ToLower is sufficient. 109 bool CaseInsensitiveEquals(std::string_view a, std::string_view b); 110 111 // Compares the two UTF16 strings in a case-insensitive manner. 112 // Use this if one of the values is a known value, and thus ToLower is sufficient. 113 bool CaseInsensitiveEquals(std::wstring_view a, std::wstring_view b); 114 115 // Returns if a UTF8 string is contained within a vector in a case-insensitive manner. 116 bool CaseInsensitiveContains(const std::vector<std::string_view>& a, std::string_view b); 117 118 // Determines if string a starts with string b. UTF8. 119 // Use this if one of the values is a known value, and thus ToLower is sufficient. 120 bool CaseInsensitiveStartsWith(std::string_view a, std::string_view b); 121 122 // Determines if string a starts with string b. UTF16. 123 // Use this if one of the values is a known value, and thus ToLower is sufficient. 124 bool CaseInsensitiveStartsWith(std::wstring_view a, std::wstring_view b); 125 126 // Determines if string a contains string b. 127 // Use this if one of the values is a known value, and thus ToLower is sufficient. 128 bool CaseInsensitiveContainsSubstring(std::string_view a, std::string_view b); 129 130 // Compares the two UTF8 strings in a case-insensitive manner, using ICU for case folding. 131 bool ICUCaseInsensitiveEquals(std::string_view a, std::string_view b); 132 133 // Determines if string a starts with string b, using ICU for case folding. 134 bool ICUCaseInsensitiveStartsWith(std::string_view a, std::string_view b); 135 136 // Returns the number of grapheme clusters (characters) in an UTF8-encoded string. 137 size_t UTF8Length(std::string_view input); 138 139 // Returns the number of units the UTF8-encoded string will take in terminal output. Some characters take 2 units in terminal output. 140 size_t UTF8ColumnWidth(const NormalizedUTF8<NormalizationC>& input); 141 142 // Returns a substring view in an UTF8-encoded string. Offset and count are measured in grapheme clusters (characters). 143 std::string_view UTF8Substring(std::string_view input, size_t offset, size_t count); 144 145 // Returns a substring view in an UTF8-encoded string trimmed to be at most expected length. Length is measured as units taken in terminal output. 146 // Note the returned substring view might be less than specified length as some characters might take 2 units in terminal output. 147 std::string UTF8TrimRightToColumnWidth(const NormalizedUTF8<NormalizationC>&, size_t expectedWidth, size_t& actualWidth); 148 149 // Get the lower case version of the given std::string 150 std::string ToLower(std::string_view in); 151 152 // Get the lower case version of the given std::wstring 153 std::wstring ToLower(std::wstring_view in); 154 155 // Folds the case of the given std::string 156 // See https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#case-folding 157 std::string FoldCase(std::string_view input); 158 159 // Folds the case of the given NormalizedString, returning it as also Normalized 160 // See https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#case-folding 161 NormalizedString FoldCase(const NormalizedString& input); 162 163 // Checks if the input string is empty or whitespace 164 bool IsEmptyOrWhitespace(std::string_view str); 165 bool IsEmptyOrWhitespace(std::wstring_view str); 166 167 // Find token in the input string and replace with value. 168 // Returns a value indicating whether a replacement occurred. 169 bool FindAndReplace(std::string& inputStr, std::string_view token, std::string_view value); 170 171 // Replaces the token in the input string with value while copying to a new result. 172 std::wstring ReplaceWhileCopying(std::wstring_view input, std::wstring_view token, std::wstring_view value); 173 174 // Removes whitespace from the beginning and end of the string. 175 std::string& Trim(std::string& str); 176 std::string Trim(std::string&& str); 177 178 // Removes whitespace from the beginning and end of the string. 179 std::wstring& Trim(std::wstring& str); 180 181 // Reads the entire stream into a string. 182 std::string ReadEntireStream(std::istream& stream); 183 184 // Reads the entire stream into a byte array. 185 std::vector<std::uint8_t> ReadEntireStreamAsByteArray(std::istream& stream); 186 187 // Expands environment variables within the input. 188 std::wstring ExpandEnvironmentVariables(const std::wstring& input); 189 190 // Converts the candidate path part into one suitable for the actual file system 191 std::string MakeSuitablePathPart(std::string_view candidate); 192 193 // Splits the file name part off of the given URI. 194 std::pair<std::string, std::filesystem::path> SplitFileNameFromURI(std::string_view uri); 195 196 // Gets the file name part of the given URI. 197 std::filesystem::path GetFileNameFromURI(std::string_view uri); 198 199 // Splits the string into words. 200 std::vector<std::string> SplitIntoWords(std::string_view input); 201 202 // Splits the string into lines. 203 // Drops empty lines. 204 std::vector<std::string> SplitIntoLines(std::string_view input, size_t maximum = 0); 205 206 // Removes lines from the vector (and/or characters from the last line) so that it contains the maximum number of lines. 207 // Returns true if changes were made, false if not. 208 bool LimitOutputLines(std::vector<std::string>& lines, size_t lineWidth, size_t maximum); 209 210 // Converts a container to a string representation of it. 211 template <typename T, typename U> 212 std::string ConvertContainerToString(const T& container, U toString) 213 { 214 std::ostringstream strstr; 215 strstr << '['; 216 217 bool firstItem = true; 218 for (const auto& item : container) 219 { 220 if (firstItem) 221 { 222 firstItem = false; 223 } 224 else 225 { 226 strstr << ", "; 227 } 228 229 strstr << toString(item); 230 } 231 232 strstr << ']'; 233 return strstr.str(); // We need C++20 to get std::move(strstr).str() to extract the string from inside the stream 234 } 235 236 template <typename T> 237 std::string ConvertContainerToString(const T& container) 238 { 239 return ConvertContainerToString(container, [](const auto& item) { return item; }); 240 } 241 242 template <typename CharType> 243 std::basic_string<CharType> StringOrEmptyIfNull(const CharType* string) 244 { 245 if (string) 246 { 247 return { string }; 248 } 249 else 250 { 251 return {}; 252 } 253 } 254 255 // Converts the given bytes into a hexadecimal string. 256 std::string ConvertToHexString(const std::vector<uint8_t>& buffer, size_t byteCount = 0); 257 258 // Converts the given hexadecimal string into bytes. 259 std::vector<uint8_t> ParseFromHexString(const std::string& value, size_t byteCount = 0); 260 261 // Join a string vector using the provided separator. 262 LocIndString Join(LocIndView separator, const std::vector<LocIndString>& vector); 263 264 // Join a string vector using the provided separator. 265 std::string Join(std::string_view separator, const std::vector<std::string>& vector); 266 267 // Splits the string using the provided separator. Entries can also be trimmed. 268 std::vector<std::string> Split(const std::string& input, char separator, bool trim = false); 269 270 // Format an input string by replacing placeholders {index} with provided values at corresponding indices. 271 // Note: After upgrading to C++20, this function should be deprecated in favor of std::format. 272 template <typename ... T> 273 std::string Format(std::string inputStr, T ... args) 274 { 275 int index = 0; 276 (FindAndReplace(inputStr, "{" + std::to_string(index++) + "}", (std::ostringstream() << args).str()),...); 277 return inputStr; 278 } 279 280 // Converts the given boolean value to a string. 281 std::string_view ConvertBoolToString(bool value); 282 283 // Converts the given GUID value to a string. 284 std::string ConvertGuidToString(const GUID& value); 285 286 // Creates a new GUID and returns the string value. 287 std::wstring CreateNewGuidNameWString(); 288 289 // Converts the input string to a DWORD value using std::stoul and returns a boolean value based on the resulting DWORD value. 290 bool IsDwordFlagSet(const std::string& value); 291 292 // Finds the next control code index that would be replaced. 293 // Returns std::string::npos if not found. 294 size_t FindControlCodeToConvert(std::string_view input, size_t offset = 0); 295 296 // Converts most control codes in the input to their corresponding control picture in the output. 297 // Exempts tab, line feed, and carriage return from being replaced. 298 std::string ConvertControlCodesToPictures(std::string_view input); 299 300 // Generates a random alpha numeric string. 301 std::string GetRandomString(size_t size = 8); 302 }