private IEnumerable <PackageProvider> FilterProvidersUsingDynamicParameters(MutableEnumerable <PackageProvider> providers, IEnumerable <PackageSource> userSpecifiedRegisteredSources, bool didUserSpecifyProviders, bool didUserSpecifySources) { var excluded = new Dictionary <string, IEnumerable <string> >(StringComparer.OrdinalIgnoreCase); var setparameters = DynamicParameterDictionary.Values.OfType <CustomRuntimeDefinedParameter>().Where(each => each.IsSet).ReEnumerable(); var matchedProviders = (setparameters.Any() ? providers.Where(p => setparameters.All(each => each.Options.Any(opt => opt.ProviderName == p.ProviderName))) : providers).ReEnumerable(); foreach (var provider in matchedProviders) { // if a 'required' parameter is not filled in, the provider should not be returned. // we'll collect these for warnings at the end of the filter. var missingRequiredParameters = DynamicParameterDictionary.Values.OfType <CustomRuntimeDefinedParameter>().Where(each => !each.IsSet && each.IsRequiredForProvider(provider.ProviderName)).ReEnumerable(); if (!missingRequiredParameters.Any()) { yield return(provider); } else { Collection <string> missingOptions = new Collection <string>(); foreach (var missingRequiredParameter in missingRequiredParameters) { foreach (var option in missingRequiredParameter.Options) { // remember these so we can warn later. missingOptions.Add(option.Name); } } excluded.Add(provider.ProviderName, missingOptions); } } /* TODO: provide errors in the case where everything got filtered out. Or maybe warnings? * * var mismatchedProviders = (setparameters.Any() ? providers.Where(each => !matchedProviders.Contains(each)).Where(p => setparameters.Any(each => each.Options.Any(opt => opt.ProviderName == p.ProviderName))) : Enumerable.Empty<PackageProvider>()).ReEnumerable(); * * if (!found) { * // we didn't find anything that matched * // they specified dynamic parameters that implicitly select providers * // that don't fit with the providers and sources that they initially asked for. * * if (didUserSpecifyProviders || didUserSpecifySources) { * * if (IsInvocation) { * QueueHeldMessage(() => Error(Errors.ExcludedProvidersDueToMissingRequiredParameter, excluded.Keys, userSpecifiedSources.JoinWithComma())); * } * * yield break; * * } * * if (didUserSpecifySources) { * // user gave sources which implied some providers but the dynamic parameters implied different providers * if (IsInvocation) { * // error * } * // return empty set * return result; * } * * // well, this is silly. * // if the user didn't specify a source or a provider * // but the FilterProvidersUsingDynamicParameters came back empty * // that means that they user specified parameters from two conflicting providers * // and they forced each other out! * * if (IsInvocation) { * // error * * } * * } */ if (ProviderName != null && ProviderName.Any()) { foreach (var providerName in ProviderName) { if (excluded.ContainsKey(providerName)) { Error(Constants.Errors.SpecifiedProviderMissingRequiredOption, providerName, excluded[providerName].JoinWithComma()); } } } // these warnings only show for providers that would have otherwise be selected. // if not for the missing requrired parameter. foreach (var mp in excluded.OrderBy(each => each.Key)) { string optionsValue = mp.Value.JoinWithComma(); if (userSpecifiedRegisteredSources.Any()) { var mp1 = mp; //Check if the provider with missing dynamic parameters has been registered with the source provided by a user var sources = userSpecifiedRegisteredSources.Where(source => source.ProviderName != null && source.ProviderName.EqualsIgnoreCase(mp1.Key)); if (didUserSpecifySources && sources.Any()) { //Error out if the provider associated with the -source matches Error(Constants.Errors.SpecifiedProviderMissingRequiredOption, mp.Key, optionsValue); } } Verbose(Constants.Messages.SkippedProviderMissingRequiredOption, mp.Key, optionsValue); } }
protected IEnumerable <SoftwareIdentity> CheckMatchedDuplicates() { // if there are overmatched packages we need to know why: // are they found across multiple providers? // are they found accross multiple sources? // are they all from the same source? foreach (var list in _resultsPerName.Values.Where(each => each != null && each.Any())) { if (list.Count == 1) { //no overmatched yield return(list.FirstOrDefault()); } else { //process the overmatched case SoftwareIdentity selectedPackage = null; var providers = list.Select(each => each.ProviderName).Distinct().ToArray(); var sources = list.Select(each => each.Source).Distinct().ToArray(); //case: a user specifies -Source and multiple packages are found. In this case to determine which one should be installed, // We treat the user's package source array is in a priority order, i.e. the first package source has the highest priority. // Of course, these packages should not be from a single source with the same provider. // Example: install-package -Source @('src1', 'src2') // install-package -Source @('src1', 'src2') -Provider @('p1', 'p2') if (Sources.Any() && (providers.Length != 1 || sources.Length != 1)) { // let's use the first source as our priority.As long as we find a package, we exit the 'for' loop righ away foreach (var source in Sources) { //select all packages matched source var pkgs = list.Where(package => source.EqualsIgnoreCase(package.Source) || (UserSpecifiedSourcesList.Keys.ContainsIgnoreCase(package.Source) && source.EqualsIgnoreCase(UserSpecifiedSourcesList[package.Source]))).ToArray(); if (pkgs.Length == 0) { continue; } if (pkgs.Length == 1) { //only one provider found the package selectedPackage = pkgs[0]; break; } if (ProviderName == null) { //user does not specify '-providerName' but we found multiple packages with a source, can not determine which one //will error out break; } if (pkgs.Length > 1) { //case: multiple providers matched the same source. //need to process provider's priority order var pkg = ProviderName.Select(p => pkgs.FirstOrDefault(each => each.ProviderName.EqualsIgnoreCase(p))).FirstOrDefault(); if (pkg != null) { selectedPackage = pkg; break; } } }//inner foreach //case: a user specifies -Provider array but no -Source and multiple packages are found. In this case to determine which one should be installed, // We treat the user's package provider array is in a priority order, i.e. the first provider in the array has the highest priority. // Of course, these packages should not be from a single source with the same provider. // Example: install-package -Provider @('p1', 'p2') } else if (ProviderName != null && ProviderName.Any() && (providers.Length != 1 || sources.Length != 1)) { foreach (var providerName in ProviderName) { //select all packages matched with the provider name var packages = list.Where(each => providerName.EqualsIgnoreCase(each.ProviderName)).ToArray(); if (packages.Length == 0) { continue; } if (packages.Length == 1) { //only one provider found the package, that's good selectedPackage = packages[0]; break; } else { //user does not specify '-source' but we found multiple packages with one provider, we can not determine which one //will error out break; } } } if (selectedPackage != null) { yield return(selectedPackage); } else { //error out for the overmatched case var suggestion = ""; if (providers.Length == 1) { // it's matching this package multiple times in the same provider. if (sources.Length == 1) { // matching it from a single source. // be more exact on matching name? or version? suggestion = Resources.Messages.SuggestRequiredVersion; } else { // it's matching the same package from multiple sources // tell them to use -source suggestion = Resources.Messages.SuggestSingleSource; } } else { // found across multiple providers // must specify -provider suggestion = Resources.Messages.SuggestSingleProviderName; } string searchKey = null; foreach (var pkg in list) { Warning(Constants.Messages.MatchesMultiplePackages, pkg.SearchKey, pkg.ProviderName, pkg.Name, pkg.Version, GetPackageSourceNameOrLocation(pkg)); searchKey = pkg.SearchKey; } Error(Constants.Errors.DisambiguateForInstall, searchKey, GetMessageString(suggestion, suggestion)); } } } }