RepositorySearch.h (17440B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #pragma once 4 #include <AppInstallerErrors.h> 5 #include <AppInstallerStrings.h> 6 #include <AppInstallerVersions.h> 7 #include <winget/LocIndependent.h> 8 #include <winget/Manifest.h> 9 10 #include <map> 11 #include <memory> 12 #include <optional> 13 #include <string> 14 #include <string_view> 15 #include <vector> 16 17 18 namespace AppInstaller::Repository 19 { 20 struct Source; 21 22 // The type of matching to perform during a search. 23 // The values must be declared in order of preference in search results. 24 enum class MatchType 25 { 26 Exact = 0, 27 CaseInsensitive, 28 StartsWith, 29 Fuzzy, 30 Substring, 31 FuzzySubstring, 32 Wildcard, 33 }; 34 35 // Convert a MatchType to a string. 36 std::string_view ToString(MatchType type); 37 38 // The field to match on. 39 // The values must be declared in order of preference in search results. 40 enum class PackageMatchField 41 { 42 Id = 0, 43 Name, 44 Moniker, 45 Command, 46 Tag, 47 PackageFamilyName, 48 ProductCode, 49 UpgradeCode, 50 NormalizedNameAndPublisher, 51 Market, 52 Unknown = 9999 53 }; 54 55 // Convert a PackageMatchField to a string. 56 std::string_view ToString(PackageMatchField matchField); 57 58 // Parse a string to PackageMatchField. 59 PackageMatchField StringToPackageMatchField(std::string_view field); 60 61 // A single match to be performed during a search. 62 struct RequestMatch 63 { 64 MatchType Type; 65 Utility::NormalizedString Value; 66 std::optional<Utility::NormalizedString> Additional; 67 68 RequestMatch(MatchType t) : Type(t) {} 69 RequestMatch(MatchType t, Utility::NormalizedString& v) : Type(t), Value(v) {} 70 RequestMatch(MatchType t, const Utility::NormalizedString& v) : Type(t), Value(v) {} 71 RequestMatch(MatchType t, Utility::NormalizedString&& v) : Type(t), Value(std::move(v)) {} 72 RequestMatch(MatchType t, std::string_view v1, std::string_view v2) : Type(t), Value(v1), Additional(Utility::NormalizedString{ v2 }) {} 73 }; 74 75 // A match on a specific field to be performed during a search. 76 struct PackageMatchFilter : public RequestMatch 77 { 78 PackageMatchField Field; 79 80 PackageMatchFilter(PackageMatchField f, MatchType t) : RequestMatch(t), Field(f) { EnsureRequiredValues(); } 81 PackageMatchFilter(PackageMatchField f, MatchType t, Utility::NormalizedString& v) : RequestMatch(t, v), Field(f) { EnsureRequiredValues(); } 82 PackageMatchFilter(PackageMatchField f, MatchType t, const Utility::NormalizedString& v) : RequestMatch(t, v), Field(f) { EnsureRequiredValues(); } 83 PackageMatchFilter(PackageMatchField f, MatchType t, Utility::NormalizedString&& v) : RequestMatch(t, std::move(v)), Field(f) { EnsureRequiredValues(); } 84 PackageMatchFilter(PackageMatchField f, MatchType t, std::string_view v1, std::string_view v2) : RequestMatch(t, v1, v2), Field(f) { EnsureRequiredValues(); } 85 86 protected: 87 void EnsureRequiredValues() 88 { 89 // Ensure that the second value always exists when it should 90 if (Field == PackageMatchField::NormalizedNameAndPublisher && !Additional) 91 { 92 Additional = Utility::NormalizedString{}; 93 } 94 } 95 }; 96 97 // The search purpose of the search request. 98 enum class SearchPurpose 99 { 100 // Default search purpose. 101 Default, 102 // The result is used for correlation to an installed package. 103 CorrelationToInstalled, 104 // The result is used for correlation to an available package. 105 CorrelationToAvailable, 106 }; 107 108 // Container for data used to filter the available manifests in a source. 109 // It can be thought of as: 110 // (Query || Inclusions...) && Filters... 111 // If Query and Inclusions are both empty, the starting data set will be the entire database. 112 // Everything && Filters... 113 struct SearchRequest 114 { 115 // The generic query matches against a source defined set of fields. 116 std::optional<RequestMatch> Query; 117 118 // Specific fields used to include more data. 119 // If Query is defined, this can add more rows afterward. 120 // If Query is not defined, this is the only set of data included. 121 std::vector<PackageMatchFilter> Inclusions; 122 123 // Specific fields used to filter the data further. 124 std::vector<PackageMatchFilter> Filters; 125 126 // The search purpose of the search request. 127 SearchPurpose Purpose = SearchPurpose::Default; 128 129 // The maximum number of results to return. 130 // The default of 0 will place no limit. 131 size_t MaximumResults{}; 132 133 // Returns a value indicating whether this request is for all available data. 134 bool IsForEverything() const; 135 136 // Returns a string summarizing the search request. 137 std::string ToString() const; 138 }; 139 140 // A property of a package version. 141 enum class PackageVersionProperty 142 { 143 Id, 144 Name, 145 SourceIdentifier, 146 SourceName, 147 Version, 148 Channel, 149 RelativePath, 150 // Returned in hexadecimal format 151 ManifestSHA256Hash, 152 Publisher, 153 ArpMinVersion, 154 ArpMaxVersion, 155 Moniker, 156 }; 157 158 // A property of a package version that can have multiple values. 159 enum class PackageVersionMultiProperty 160 { 161 // The package family names (PFN) associated with the package version 162 PackageFamilyName, 163 // The product codes associated with the package version. 164 ProductCode, 165 // The upgrade codes associated with the package version. 166 UpgradeCode, 167 // TODO: Fully implement these 3; the data is not yet in the index source (name and publisher are hacks and locale is not present) 168 // For future usage of these, be aware of the limitations. 169 // The package names for the version; ideally these would match in number and order with both Publisher and Locale. 170 Name, 171 // The publisher values for the version; ideally these would match in number and order with both Name and Locale. 172 Publisher, 173 // The locale of the matching Name and Publisher values; ideally these would match in number and order with both Name and Publisher. 174 // May be empty if there is only a single value for Name and Publisher. 175 Locale, 176 // The tags associated with a package version. 177 Tag, 178 // The commands associated with a package version. 179 Command, 180 }; 181 182 // A metadata item of a package version. These values are persisted and cannot be changed. 183 enum class PackageVersionMetadata : int32_t 184 { 185 // The InstallerType of an installed package 186 InstalledType, 187 // The Scope of an installed package 188 InstalledScope, 189 // The system path where the package is installed 190 InstalledLocation, 191 // The standard uninstall command; which may be interactive 192 StandardUninstallCommand, 193 // An uninstall command that should be non-interactive 194 SilentUninstallCommand, 195 // The publisher of the package 196 Publisher, 197 // The locale of the package 198 InstalledLocale, 199 // The write time for the given version 200 TrackingWriteTime, 201 // The Architecture of an installed package 202 InstalledArchitecture, 203 // The pinned state of the installed package 204 // As a package can have multiple pins for multiple sources, this is the strictest pin 205 PinnedState, 206 // The Architecture of user intent 207 UserIntentArchitecture, 208 // The locale of user intent 209 UserIntentLocale, 210 // The standard modify command; which may be interactive 211 StandardModifyCommand, 212 // No Modify flag 213 NoModify, 214 // No Repair flag 215 NoRepair, 216 }; 217 218 // Convert a PackageVersionMetadata to a string. 219 std::string_view ToString(PackageVersionMetadata pvm); 220 221 // A single package version. 222 struct IPackageVersion 223 { 224 using Metadata = std::map<PackageVersionMetadata, std::string>; 225 226 virtual ~IPackageVersion() = default; 227 228 // Gets a property of this package version. 229 virtual Utility::LocIndString GetProperty(PackageVersionProperty property) const = 0; 230 231 // Gets a property of this package version that can have multiple values. 232 virtual std::vector<Utility::LocIndString> GetMultiProperty(PackageVersionMultiProperty property) const = 0; 233 234 // Gets the manifest of this package version. 235 virtual Manifest::Manifest GetManifest() = 0; 236 237 // Gets the source where this package version is from. 238 virtual Source GetSource() const = 0; 239 240 // Gets any metadata associated with this package version. 241 // Primarily stores data on installed packages. 242 virtual Metadata GetMetadata() const = 0; 243 }; 244 245 // A key to identify a package version within a package. 246 struct PackageVersionKey 247 { 248 PackageVersionKey() = default; 249 250 PackageVersionKey(std::string sourceId, Utility::NormalizedString version, Utility::NormalizedString channel) : 251 SourceId(std::move(sourceId)), Version(std::move(version)), Channel(std::move(channel)) {} 252 253 // The source id that this version came from. 254 std::string SourceId; 255 256 // The version. 257 Utility::NormalizedString Version; 258 259 // The channel. 260 Utility::NormalizedString Channel; 261 262 bool operator<(const PackageVersionKey& other) const 263 { 264 // Sort using only the version and channel. 265 // The order for the sources depends on the context. 266 return Utility::VersionAndChannel({ Version }, { Channel }) < Utility::VersionAndChannel({ other.Version }, { other.Channel }); 267 } 268 269 // Determines if a well defined key (this one) is matched by the provided key. 270 // The provided key may use empty values to indicate no specific matching requirements. 271 bool IsMatch(const PackageVersionKey& other) const; 272 273 // Determines if this version is the simple "latest" targeting version (version and channel are both empty). 274 bool IsDefaultLatest() const; 275 }; 276 277 // A property of a package. 278 enum class PackageProperty 279 { 280 Id, 281 Name, 282 }; 283 284 // A property of a package that can have multiple values. 285 enum class PackageMultiProperty 286 { 287 // The package family names (PFN) associated with the package. 288 PackageFamilyName, 289 // The product codes associated with the package. 290 ProductCode, 291 // The upgrade codes associated with the package. 292 UpgradeCode, 293 // The normalized names for the package. 294 NormalizedName, 295 // The normalized publisher names for the package. 296 NormalizedPublisher, 297 // The tags associated with the package. 298 Tag, 299 // The commands associated with the package. 300 Command, 301 }; 302 303 // Maps the package multi-property value to its package version multi-property value for internal use. 304 PackageVersionMultiProperty PackageMultiPropertyToPackageVersionMultiProperty(PackageMultiProperty property); 305 306 // To allow for runtime casting from IPackage to the specific types, this enum contains all of the IPackage implementations. 307 enum class IPackageType 308 { 309 TestPackage, 310 RestPackage, 311 SQLitePackage1, 312 SQLitePackage2, 313 PinnablePackage, 314 CompositeInstalledPackage, 315 }; 316 317 // Contains a collection of package versions. 318 struct IPackageVersionCollection 319 { 320 virtual ~IPackageVersionCollection() = default; 321 322 // Gets all versions of this package. 323 // The versions will be returned in sorted, descending order. 324 // Ex. { 4, 3, 2, 1 } 325 virtual std::vector<PackageVersionKey> GetVersionKeys() const = 0; 326 327 // Gets a specific version of this package. 328 virtual std::shared_ptr<IPackageVersion> GetVersion(const PackageVersionKey& versionKey) const = 0; 329 330 // A convenience method to effectively call `GetVersion(GetVersionKeys[0])`. 331 virtual std::shared_ptr<IPackageVersion> GetLatestVersion() const = 0; 332 }; 333 334 // Contains information about a package and its versions from a single source. 335 struct IPackage : public IPackageVersionCollection 336 { 337 virtual ~IPackage() = default; 338 339 // Gets a property of this package. 340 virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; 341 342 // Gets a property of this package that can have multiple values. 343 virtual std::vector<Utility::LocIndString> GetMultiProperty(PackageMultiProperty property) const = 0; 344 345 // Gets the source that this package is from. 346 virtual Source GetSource() const = 0; 347 348 // Determines if the given IPackage refers to the same package as this one. 349 virtual bool IsSame(const IPackage*) const = 0; 350 351 // Gets this object as the requested type, or null if it is not the requested type. 352 virtual const void* CastTo(IPackageType type) const = 0; 353 }; 354 355 // Contains information about the graph of packages related to a search. 356 struct ICompositePackage 357 { 358 virtual ~ICompositePackage() = default; 359 360 // Gets a property of this package result. 361 virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; 362 363 // Gets the installed package information. 364 virtual std::shared_ptr<IPackage> GetInstalled() = 0; 365 366 // Gets all of the available packages for this result. 367 // There will be at most one package per source in this list. 368 virtual std::vector<std::shared_ptr<IPackage>> GetAvailable() = 0; 369 }; 370 371 // Does the equivalent of a dynamic_cast, but without it to allow RTTI to be disabled. 372 // Example usage: 373 // bool IsSame(const IPackage* other) const override 374 // { 375 // const MyPackage* otherAsMyType = PackageCast<const MyPackage*>(other); 376 // ... 377 // } 378 template <typename PackageType> 379 PackageType PackageCast(const IPackage* package) 380 { 381 static_assert(std::is_pointer_v<PackageType>, "The target type of the PackageCast must be a pointer; use the same type as if this were a dynamic_cast."); 382 if (!package) 383 { 384 return nullptr; 385 } 386 using ActualPackageType = std::remove_pointer_t<std::remove_cv_t<PackageType>>; 387 return reinterpret_cast<PackageType>(package->CastTo(ActualPackageType::PackageType)); 388 } 389 390 // A single result from the search. 391 struct ResultMatch 392 { 393 // The package found by the search request. 394 std::shared_ptr<ICompositePackage> Package; 395 396 // The highest order field on which the package matched the search. 397 PackageMatchFilter MatchCriteria; 398 399 ResultMatch(std::shared_ptr<ICompositePackage> p, PackageMatchFilter f) : Package(std::move(p)), MatchCriteria(std::move(f)) {} 400 }; 401 402 // Search result data. 403 struct SearchResult 404 { 405 // Contains a failure from the Search. 406 struct Failure 407 { 408 std::string SourceName; 409 std::exception_ptr Exception; 410 }; 411 412 // The full set of results from the search. 413 std::vector<ResultMatch> Matches; 414 415 // If true, the results were truncated by the given SearchRequest::MaximumResults. 416 bool Truncated = false; 417 418 // Present if the Search was against a composite source and one failed, but not limited to that scenario. 419 std::vector<Failure> Failures; 420 }; 421 422 struct UnsupportedRequestException : public wil::ResultException 423 { 424 UnsupportedRequestException() : wil::ResultException(APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST) {} 425 426 UnsupportedRequestException( 427 std::vector<std::string> unsupportedPackageMatchFields, 428 std::vector<std::string> requiredPackageMatchFields, 429 std::vector<std::string> unsupportedQueryParameters, 430 std::vector<std::string> requiredQueryParameters) : 431 wil::ResultException(APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST), 432 UnsupportedPackageMatchFields(std::move(unsupportedPackageMatchFields)), RequiredPackageMatchFields(std::move(requiredPackageMatchFields)), 433 UnsupportedQueryParameters(std::move(unsupportedQueryParameters)), RequiredQueryParameters(std::move(requiredQueryParameters)) {} 434 435 std::vector<std::string> UnsupportedPackageMatchFields; 436 std::vector<std::string> RequiredPackageMatchFields; 437 std::vector<std::string> UnsupportedQueryParameters; 438 std::vector<std::string> RequiredQueryParameters; 439 440 const char* what() const noexcept override; 441 442 private: 443 mutable std::string m_whatMessage; 444 }; 445 }