зеркало из https://github.com/microsoft/PSRule.git
Refactoring of language scopes (#2601)
This commit is contained in:
Родитель
ea4a77c3ab
Коммит
d2c419126c
|
@ -32,8 +32,8 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
|
|||
[#2552](https://github.com/microsoft/PSRule/issues/2552)
|
||||
- Modules are automatically restored unless `--no-restore` is used with the `run` command.
|
||||
- Engineering:
|
||||
- Bump YamlDotNet to v16.1.3.
|
||||
[#1874](https://github.com/microsoft/PSRule/pull/1874)
|
||||
- Bump YamlDotNet to v16.2.0.
|
||||
[#2596](https://github.com/microsoft/PSRule/pull/2596)
|
||||
|
||||
## v3.0.0-B0275 (pre-release)
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ nav:
|
|||
- Index: concepts/cli/index.md
|
||||
- run: concepts/cli/run.md
|
||||
- module: concepts/cli/module.md
|
||||
- restore: concepts/cli/restore.md
|
||||
- Assertion methods: concepts/PSRule/en-US/about_PSRule_Assert.md
|
||||
- Baselines: concepts/PSRule/en-US/about_PSRule_Baseline.md
|
||||
- Badges: concepts/PSRule/en-US/about_PSRule_Badges.md
|
||||
|
|
|
@ -658,8 +658,7 @@ internal static class HostHelper
|
|||
|
||||
private static SuppressionGroupV1[] ToSuppressionGroupV1(IEnumerable<ILanguageBlock> blocks, RunspaceContext context)
|
||||
{
|
||||
if (blocks == null)
|
||||
return Array.Empty<SuppressionGroupV1>();
|
||||
if (blocks == null) return [];
|
||||
|
||||
// Index suppression groups by Id.
|
||||
var results = new Dictionary<string, SuppressionGroupV1>(StringComparer.OrdinalIgnoreCase);
|
||||
|
@ -682,13 +681,12 @@ internal static class HostHelper
|
|||
context.ExitLanguageScope(block.Source);
|
||||
}
|
||||
}
|
||||
return results.Values.ToArray();
|
||||
return [.. results.Values];
|
||||
}
|
||||
|
||||
private static ModuleConfigV1[] ToModuleConfigV1(IEnumerable<ILanguageBlock> blocks, RunspaceContext context)
|
||||
{
|
||||
if (blocks == null)
|
||||
return Array.Empty<ModuleConfigV1>();
|
||||
if (blocks == null) return [];
|
||||
|
||||
// Index configurations by Name.
|
||||
var results = new Dictionary<string, ModuleConfigV1>(StringComparer.OrdinalIgnoreCase);
|
||||
|
@ -766,39 +764,86 @@ internal static class HostHelper
|
|||
|
||||
private static bool Match(RunspaceContext context, RuleBlock resource)
|
||||
{
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Rule);
|
||||
return filter == null || filter.Match(resource);
|
||||
try
|
||||
{
|
||||
context.EnterLanguageScope(resource.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Rule);
|
||||
return filter == null || filter.Match(resource);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExitLanguageScope(resource.Source);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Match(RunspaceContext context, IRuleV1 resource)
|
||||
{
|
||||
context.EnterLanguageScope(resource.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Rule);
|
||||
return filter == null || filter.Match(resource);
|
||||
try
|
||||
{
|
||||
context.EnterLanguageScope(resource.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Rule);
|
||||
return filter == null || filter.Match(resource);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExitLanguageScope(resource.Source);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Match(RunspaceContext context, Baseline resource)
|
||||
{
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Baseline);
|
||||
return filter == null || filter.Match(resource);
|
||||
try
|
||||
{
|
||||
context.EnterLanguageScope(resource.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Baseline);
|
||||
return filter == null || filter.Match(resource);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExitLanguageScope(resource.Source);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Match(RunspaceContext context, ScriptBlockConvention block)
|
||||
{
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Convention);
|
||||
return filter == null || filter.Match(block);
|
||||
try
|
||||
{
|
||||
context.EnterLanguageScope(block.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Convention);
|
||||
return filter == null || filter.Match(block);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExitLanguageScope(block.Source);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Match(RunspaceContext context, SelectorV1 resource)
|
||||
{
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Selector);
|
||||
return filter == null || filter.Match(resource);
|
||||
try
|
||||
{
|
||||
context.EnterLanguageScope(resource.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Selector);
|
||||
return filter == null || filter.Match(resource);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExitLanguageScope(resource.Source);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Match(RunspaceContext context, SuppressionGroupV1 suppressionGroup)
|
||||
{
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.SuppressionGroup);
|
||||
return filter == null || filter.Match(suppressionGroup);
|
||||
try
|
||||
{
|
||||
context.EnterLanguageScope(suppressionGroup.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.SuppressionGroup);
|
||||
return filter == null || filter.Match(suppressionGroup);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//context.ExitLanguageScope(suppressionGroup.Source);
|
||||
}
|
||||
}
|
||||
|
||||
private static IConvention[] Sort(RunspaceContext context, IConvention[] conventions)
|
||||
|
|
|
@ -66,6 +66,11 @@ internal sealed class PipelineContext : IPipelineContext, IBindingContext
|
|||
|
||||
public IPipelineWriter Writer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A collection of languages scopes for this pipeline.
|
||||
/// </summary>
|
||||
public ILanguageScopeCollection LanguageScopes { get; }
|
||||
|
||||
private PipelineContext(PSRuleOption option, IHostContext hostContext, PipelineInputStream reader, IPipelineWriter writer, OptionContextBuilder optionBuilder, IList<ResourceRef> unresolved)
|
||||
{
|
||||
_OptionBuilder = optionBuilder;
|
||||
|
@ -87,6 +92,7 @@ internal sealed class PipelineContext : IPipelineContext, IBindingContext
|
|||
RunId = Environment.GetRunId() ?? ObjectHashAlgorithm.GetDigest(Guid.NewGuid().ToByteArray());
|
||||
RunTime = Stopwatch.StartNew();
|
||||
_DefaultOptionContext = _OptionBuilder?.Build(null);
|
||||
LanguageScopes = new LanguageScopeSet();
|
||||
}
|
||||
|
||||
public static PipelineContext New(PSRuleOption option, IHostContext hostContext, PipelineInputStream reader, IPipelineWriter writer, OptionContextBuilder optionBuilder, IList<ResourceRef> unresolved)
|
||||
|
@ -299,6 +305,7 @@ internal sealed class PipelineContext : IPipelineContext, IBindingContext
|
|||
ObjectHashAlgorithm?.Dispose();
|
||||
_Runspace?.Dispose();
|
||||
_PathExpressionCache.Clear();
|
||||
LanguageScopes.Dispose();
|
||||
LocalizedDataCache.Clear();
|
||||
ExpressionCache.Clear();
|
||||
ContentCache.Clear();
|
||||
|
|
|
@ -25,7 +25,7 @@ internal interface ILanguageScope : IDisposable
|
|||
/// <summary>
|
||||
/// Get an ordered culture preference list which will be tries for finding help.
|
||||
/// </summary>
|
||||
string[] Culture { get; }
|
||||
string[]? Culture { get; }
|
||||
|
||||
void Configure(OptionContext context);
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Runtime;
|
||||
|
||||
internal interface ILanguageScopeCollection : IDisposable
|
||||
{
|
||||
IEnumerable<ILanguageScope> Get();
|
||||
|
||||
bool TryScope(string name, out ILanguageScope scope);
|
||||
|
||||
bool Import(string name);
|
||||
}
|
|
@ -24,6 +24,7 @@ internal sealed class LanguageScope : ILanguageScope
|
|||
|
||||
public LanguageScope(string name)
|
||||
{
|
||||
_Configuration = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
Name = ResourceHelper.NormalizeScope(name);
|
||||
_Filter = [];
|
||||
_Service = [];
|
||||
|
@ -33,7 +34,7 @@ internal sealed class LanguageScope : ILanguageScope
|
|||
public string Name { [DebuggerStepThrough] get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string[] Culture { [DebuggerStepThrough] get; [DebuggerStepThrough] private set; }
|
||||
public string[]? Culture { [DebuggerStepThrough] get; [DebuggerStepThrough] private set; }
|
||||
|
||||
public StringComparer GetBindingComparer() => _BindingComparer ?? StringComparer.OrdinalIgnoreCase;
|
||||
|
||||
|
@ -50,6 +51,7 @@ internal sealed class LanguageScope : ILanguageScope
|
|||
if (context == null) throw new ArgumentNullException(nameof(context));
|
||||
|
||||
_Configuration = context.Configuration;
|
||||
_Configuration ??= new Dictionary<string, object>();
|
||||
WithFilter(context.RuleFilter);
|
||||
WithFilter(context.ConventionFilter);
|
||||
_BindingComparer = context.Binding.GetComparer();
|
||||
|
@ -62,7 +64,7 @@ internal sealed class LanguageScope : ILanguageScope
|
|||
/// <inheritdoc/>
|
||||
public bool TryConfigurationValue(string key, out object? value)
|
||||
{
|
||||
value = null;
|
||||
value = default;
|
||||
return !string.IsNullOrEmpty(key) && _Configuration != null && _Configuration.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
|
@ -113,8 +115,8 @@ internal sealed class LanguageScope : ILanguageScope
|
|||
path = result.TargetTypePath;
|
||||
return true;
|
||||
}
|
||||
type = null;
|
||||
path = null;
|
||||
type = default;
|
||||
path = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -128,8 +130,8 @@ internal sealed class LanguageScope : ILanguageScope
|
|||
path = result.TargetNamePath;
|
||||
return true;
|
||||
}
|
||||
name = null;
|
||||
path = null;
|
||||
name = default;
|
||||
path = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace PSRule.Runtime;
|
|||
/// <summary>
|
||||
/// A collection of <see cref="ILanguageScope"/>.
|
||||
/// </summary>
|
||||
internal sealed class LanguageScopeSet : IDisposable
|
||||
internal sealed class LanguageScopeSet : ILanguageScopeCollection
|
||||
{
|
||||
private readonly Dictionary<string, ILanguageScope> _Scopes;
|
||||
|
||||
|
@ -18,15 +18,7 @@ internal sealed class LanguageScopeSet : IDisposable
|
|||
public LanguageScopeSet()
|
||||
{
|
||||
_Scopes = new Dictionary<string, ILanguageScope>(StringComparer.OrdinalIgnoreCase);
|
||||
Import(null, out _Current);
|
||||
}
|
||||
|
||||
public ILanguageScope Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Current;
|
||||
}
|
||||
Import(null);
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
@ -64,34 +56,22 @@ internal sealed class LanguageScopeSet : IDisposable
|
|||
_Scopes.Add(languageScope.Name, languageScope);
|
||||
}
|
||||
|
||||
internal IEnumerable<ILanguageScope> Get()
|
||||
public IEnumerable<ILanguageScope> Get()
|
||||
{
|
||||
return _Scopes.Values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switch to a specific language scope by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the language scope to switch to.</param>
|
||||
internal void UseScope(string name)
|
||||
{
|
||||
if (!_Scopes.TryGetValue(GetScopeName(name), out var scope))
|
||||
throw new Exception($"The specified scope '{name}' was not found.");
|
||||
|
||||
_Current = scope;
|
||||
}
|
||||
|
||||
internal bool TryScope(string name, out ILanguageScope scope)
|
||||
public bool TryScope(string name, out ILanguageScope scope)
|
||||
{
|
||||
return _Scopes.TryGetValue(GetScopeName(name), out scope);
|
||||
}
|
||||
|
||||
internal bool Import(string name, out ILanguageScope scope)
|
||||
public bool Import(string name)
|
||||
{
|
||||
if (_Scopes.TryGetValue(GetScopeName(name), out scope))
|
||||
if (_Scopes.ContainsKey(GetScopeName(name)))
|
||||
return false;
|
||||
|
||||
scope = new LanguageScope(name);
|
||||
var scope = new LanguageScope(name);
|
||||
Add(scope);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -65,10 +65,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
private readonly List<ResultReason> _Reason;
|
||||
private readonly List<IConvention> _Conventions;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of languages scopes for this pipeline.
|
||||
/// </summary>
|
||||
private readonly LanguageScopeSet _LanguageScopes;
|
||||
private ILanguageScope? _CurrentLanguageScope;
|
||||
|
||||
// Track whether Dispose has been called.
|
||||
private bool _Disposed;
|
||||
|
@ -91,7 +88,6 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
_RuleTimer = new Stopwatch();
|
||||
_Reason = [];
|
||||
_Conventions = [];
|
||||
_LanguageScopes = new LanguageScopeSet();
|
||||
_Scope = new Stack<RunspaceScope>();
|
||||
}
|
||||
|
||||
|
@ -105,12 +101,12 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
|
||||
internal SourceScope? Source { get; private set; }
|
||||
|
||||
internal ILanguageScope LanguageScope
|
||||
internal ILanguageScope? LanguageScope
|
||||
{
|
||||
[DebuggerStepThrough]
|
||||
get
|
||||
{
|
||||
return _LanguageScopes.Current;
|
||||
return _CurrentLanguageScope;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -524,7 +520,10 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
if (!file.Exists())
|
||||
throw new FileNotFoundException(PSRuleResources.ScriptNotFound, file.Path);
|
||||
|
||||
_LanguageScopes.UseScope(file.Module);
|
||||
if (!Pipeline.LanguageScopes.TryScope(file.Module, out var scope))
|
||||
throw new Exception("Language scope is unknown.");
|
||||
|
||||
_CurrentLanguageScope = scope;
|
||||
|
||||
if (TargetObject != null && LanguageScope != null)
|
||||
Binding = LanguageScope.Bind(TargetObject);
|
||||
|
@ -535,6 +534,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
public void ExitLanguageScope(ISourceFile file)
|
||||
{
|
||||
// Look at scope popping and validation.
|
||||
_CurrentLanguageScope = null;
|
||||
|
||||
Source = null;
|
||||
}
|
||||
|
@ -645,6 +645,8 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
|
||||
internal void AddService(string id, object service)
|
||||
{
|
||||
if (LanguageScope == null) throw new Exception("Can not call out of scope.");
|
||||
|
||||
ResourceHelper.ParseIdString(LanguageScope.Name, id, out var scopeName, out var name);
|
||||
if (!StringComparer.OrdinalIgnoreCase.Equals(LanguageScope.Name, scopeName) || string.IsNullOrEmpty(name))
|
||||
return;
|
||||
|
@ -654,8 +656,10 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
|
||||
internal object? GetService(string id)
|
||||
{
|
||||
if (LanguageScope == null) throw new Exception("Can not call out of scope.");
|
||||
|
||||
ResourceHelper.ParseIdString(LanguageScope.Name, id, out var scopeName, out var name);
|
||||
return !_LanguageScopes.TryScope(scopeName, out var scope) || string.IsNullOrEmpty(name) ? null : scope.GetService(name!);
|
||||
return !Pipeline.LanguageScopes.TryScope(scopeName, out var scope) || string.IsNullOrEmpty(name) ? null : scope.GetService(name!);
|
||||
}
|
||||
|
||||
private void RunConventionInitialize()
|
||||
|
@ -722,7 +726,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
foreach (var resource in resources.Where(r => r.Kind == ResourceKind.ModuleConfig).ToArray())
|
||||
Pipeline.Import(this, resource);
|
||||
|
||||
foreach (var languageScope in _LanguageScopes.Get())
|
||||
foreach (var languageScope in Pipeline.LanguageScopes.Get())
|
||||
Pipeline.UpdateLanguageScope(languageScope);
|
||||
|
||||
foreach (var resource in resources)
|
||||
|
@ -745,14 +749,14 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
foreach (var resource in resources.Where(r => r.Kind != ResourceKind.ModuleConfig).ToArray())
|
||||
Pipeline.Import(this, resource);
|
||||
|
||||
foreach (var languageScope in _LanguageScopes.Get())
|
||||
foreach (var languageScope in Pipeline.LanguageScopes.Get())
|
||||
Pipeline.UpdateLanguageScope(languageScope);
|
||||
}
|
||||
|
||||
private void InitLanguageScopes(Source[] source)
|
||||
{
|
||||
for (var i = 0; source != null && i < source.Length; i++)
|
||||
_LanguageScopes.Import(source[i].Scope, out _);
|
||||
Pipeline.LanguageScopes.Import(source[i].Scope);
|
||||
}
|
||||
|
||||
public void Begin()
|
||||
|
@ -788,7 +792,7 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
if (string.IsNullOrEmpty(Source?.File.HelpPath))
|
||||
return null;
|
||||
|
||||
var cultures = LanguageScope.Culture;
|
||||
var cultures = LanguageScope?.Culture;
|
||||
if (!_RaisedUsingInvariantCulture && (cultures == null || cultures.Length == 0))
|
||||
{
|
||||
this.Throw(_InvariantCulture, PSRuleResources.UsingInvariantCulture);
|
||||
|
@ -907,7 +911,6 @@ internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDis
|
|||
if (_Conventions[i] is IDisposable d)
|
||||
d.Dispose();
|
||||
}
|
||||
_LanguageScopes.Dispose();
|
||||
}
|
||||
_Disposed = true;
|
||||
}
|
||||
|
|
|
@ -625,6 +625,7 @@ public sealed class FunctionTests
|
|||
context.Init(s);
|
||||
context.Begin();
|
||||
context.PushScope(Runtime.RunspaceScope.Precondition);
|
||||
context.EnterLanguageScope(s[0].File[0]);
|
||||
context.EnterTargetObject(new TargetObject(targetObject));
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ public sealed class PSRuleOptionTests : BaseTests
|
|||
var context = new Runtime.RunspaceContext(PipelineContext.New(option, null, null, new TestWriter(GetOption()), builder, null));
|
||||
context.Init(null);
|
||||
context.Begin();
|
||||
context.EnterLanguageScope(GetSource()[0].File[0]);
|
||||
return new Runtime.Configuration(context);
|
||||
}
|
||||
|
||||
|
|
|
@ -1873,6 +1873,7 @@ public sealed class SelectorTests : BaseTests
|
|||
context.Init(source);
|
||||
context.Begin();
|
||||
var selector = HostHelper.GetSelectorForTests(source, context).ToArray().FirstOrDefault(s => s.Name == name);
|
||||
context.EnterLanguageScope(selector.Source);
|
||||
return new SelectorVisitor(context, selector.Id, selector.Source, selector.Spec.If);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче