mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
177 lines
7.1 KiB
C#
177 lines
7.1 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Management.Automation;
|
|
using System.Management.Automation.Internal;
|
|
|
|
namespace Microsoft.PowerShell.Commands
|
|
{
|
|
/// <summary>
|
|
/// Implements Get-ExperimentalFeature cmdlet.
|
|
/// </summary>
|
|
[Cmdlet(VerbsCommon.Get, "ExperimentalFeature", HelpUri = "")]
|
|
public class GetExperimentalFeatureCommand : PSCmdlet
|
|
{
|
|
/// <summary>
|
|
/// Specify the feature names.
|
|
/// </summary>
|
|
[Parameter(ValueFromPipeline = true, Position = 0)]
|
|
[ValidateNotNullOrEmpty]
|
|
public string[] Name { get; set; }
|
|
|
|
/// <summary>
|
|
/// Search module paths to find all available experimental features.
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter ListAvailable { get; set; }
|
|
|
|
/// <summary>
|
|
/// ProcessRecord method of this cmdlet.
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant;
|
|
IEnumerable<WildcardPattern> namePatterns = SessionStateUtilities.CreateWildcardsFromStrings(Name, wildcardOptions);
|
|
|
|
if (ListAvailable)
|
|
{
|
|
foreach (ExperimentalFeature feature in GetAvailableExperimentalFeatures(namePatterns).OrderBy(GetSortingString))
|
|
{
|
|
WriteObject(feature);
|
|
}
|
|
}
|
|
else if (ExperimentalFeature.EnabledExperimentalFeatureNames.Count > 0)
|
|
{
|
|
foreach (ExperimentalFeature feature in GetEnabledExperimentalFeatures(namePatterns).OrderBy(GetSortingString))
|
|
{
|
|
WriteObject(feature);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct the string for sorting experimental feature records.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Engine features come before module features.
|
|
/// Within engine features and more features, features are ordered by name.
|
|
/// </remarks>
|
|
private static string GetSortingString(ExperimentalFeature feature)
|
|
{
|
|
if (ExperimentalFeature.EngineSource.Equals(feature.Source))
|
|
{
|
|
return "0:" + feature.Name;
|
|
}
|
|
return "1:" + feature.Name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get enabled experimental features based on the specified name patterns.
|
|
/// </summary>
|
|
private IEnumerable<ExperimentalFeature> GetEnabledExperimentalFeatures(IEnumerable<WildcardPattern> namePatterns)
|
|
{
|
|
var moduleFeatures = new List<string>();
|
|
var moduleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (string featureName in ExperimentalFeature.EnabledExperimentalFeatureNames)
|
|
{
|
|
// Only process the feature names that matches any name patterns.
|
|
if (SessionStateUtilities.MatchesAnyWildcardPattern(featureName, namePatterns, defaultValue: true))
|
|
{
|
|
if (ExperimentalFeature.EngineExperimentalFeatureMap.TryGetValue(featureName, out ExperimentalFeature feature))
|
|
{
|
|
yield return feature;
|
|
}
|
|
else
|
|
{
|
|
moduleFeatures.Add(featureName);
|
|
int lastDotIndex = featureName.LastIndexOf('.');
|
|
moduleNames.Add(featureName.Substring(0, lastDotIndex));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (moduleFeatures.Count > 0)
|
|
{
|
|
var featuresFromGivenModules = new Dictionary<string, ExperimentalFeature>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (string moduleFile in GetValidModuleFiles(moduleNames))
|
|
{
|
|
foreach (var feature in ModuleIntrinsics.GetExperimentalFeature(moduleFile))
|
|
{
|
|
featuresFromGivenModules.TryAdd(feature.Name, feature);
|
|
}
|
|
}
|
|
|
|
foreach (string featureName in moduleFeatures)
|
|
{
|
|
if (featuresFromGivenModules.TryGetValue(featureName, out ExperimentalFeature feature))
|
|
{
|
|
yield return feature;
|
|
}
|
|
else
|
|
{
|
|
yield return new ExperimentalFeature(featureName, description: null, source: null, isEnabled: true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get available experimental features based on the specified name patterns.
|
|
/// </summary>
|
|
private IEnumerable<ExperimentalFeature> GetAvailableExperimentalFeatures(IEnumerable<WildcardPattern> namePatterns)
|
|
{
|
|
foreach (ExperimentalFeature feature in ExperimentalFeature.EngineExperimentalFeatures)
|
|
{
|
|
if (SessionStateUtilities.MatchesAnyWildcardPattern(feature.Name, namePatterns, defaultValue: true))
|
|
{
|
|
yield return feature;
|
|
}
|
|
}
|
|
|
|
foreach (string moduleFile in GetValidModuleFiles(moduleNamesToFind: null))
|
|
{
|
|
ExperimentalFeature[] features = ModuleIntrinsics.GetExperimentalFeature(moduleFile);
|
|
foreach (var feature in features)
|
|
{
|
|
if (SessionStateUtilities.MatchesAnyWildcardPattern(feature.Name, namePatterns, defaultValue: true))
|
|
{
|
|
yield return feature;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get valid module files from module paths.
|
|
/// </summary>
|
|
private IEnumerable<string> GetValidModuleFiles(HashSet<string> moduleNamesToFind)
|
|
{
|
|
var modulePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (string path in ModuleIntrinsics.GetModulePath(includeSystemModulePath: false, Context))
|
|
{
|
|
string uniquePath = path.TrimEnd(Utils.Separators.Directory);
|
|
if (!modulePaths.Add(uniquePath)) { continue; }
|
|
|
|
foreach (string moduleFile in ModuleUtils.GetDefaultAvailableModuleFiles(uniquePath))
|
|
{
|
|
// We only care about module manifest files because that's where experimental features are declared.
|
|
if (!moduleFile.EndsWith(StringLiterals.PowerShellDataFileExtension, StringComparison.OrdinalIgnoreCase)) { continue; }
|
|
|
|
if (moduleNamesToFind != null)
|
|
{
|
|
string currentModuleName = ModuleIntrinsics.GetModuleName(moduleFile);
|
|
if (!moduleNamesToFind.Contains(currentModuleName)) { continue; }
|
|
}
|
|
|
|
yield return moduleFile;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|