зеркало из https://github.com/microsoft/PSRule.git
Refactoring (#1852)
This commit is contained in:
Родитель
3b6be55376
Коммит
cdc08bb115
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System.CommandLine.Invocation;
|
using System.CommandLine.Invocation;
|
||||||
using PSRule.Configuration;
|
using PSRule.Configuration;
|
||||||
using PSRule.Options;
|
|
||||||
|
|
||||||
namespace PSRule.CommandLine;
|
namespace PSRule.CommandLine;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
namespace PSRule.Emitters
|
namespace PSRule.Emitters;
|
||||||
|
|
||||||
|
internal class InternalFileStream
|
||||||
{
|
{
|
||||||
internal class InternalFileStream
|
public InternalFileStream()
|
||||||
{
|
{
|
||||||
public InternalFileStream()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Badges;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An instance of a badge created by the Badge API.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class Badge : IBadge
|
||||||
|
{
|
||||||
|
private readonly string _LeftText;
|
||||||
|
private readonly string _RightText;
|
||||||
|
private readonly double _LeftWidth;
|
||||||
|
private readonly double _RightWidth;
|
||||||
|
private readonly int _MidPadding;
|
||||||
|
private readonly int _BorderPadding;
|
||||||
|
private readonly string _Fill;
|
||||||
|
|
||||||
|
internal Badge(string left, string right, string fill)
|
||||||
|
{
|
||||||
|
_LeftWidth = BadgeResources.Measure(left);
|
||||||
|
_RightWidth = BadgeResources.Measure(right);
|
||||||
|
|
||||||
|
_LeftText = left;
|
||||||
|
_RightText = right;
|
||||||
|
_MidPadding = 3;
|
||||||
|
_BorderPadding = 7;
|
||||||
|
_Fill = fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return ToSvg();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string ToSvg()
|
||||||
|
{
|
||||||
|
var w = (int)Math.Round(_LeftWidth + _RightWidth + 2 * _BorderPadding + 2 * _MidPadding);
|
||||||
|
var x = (int)Math.Round(_LeftWidth + _BorderPadding + _MidPadding);
|
||||||
|
|
||||||
|
var builder = new SvgBuilder(
|
||||||
|
width: w,
|
||||||
|
height: 20,
|
||||||
|
textScale: 10,
|
||||||
|
midPoint: x,
|
||||||
|
rounding: 2,
|
||||||
|
borderPadding: _BorderPadding,
|
||||||
|
midPadding: _MidPadding);
|
||||||
|
builder.Begin(string.Concat(_LeftText, ": ", _RightText));
|
||||||
|
builder.Backfill(_Fill);
|
||||||
|
builder.TextBlock(_LeftText, _RightText, 110);
|
||||||
|
builder.End();
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ToFile(string path)
|
||||||
|
{
|
||||||
|
path = Environment.GetRootedPath(path);
|
||||||
|
var parentPath = Directory.GetParent(path);
|
||||||
|
if (!parentPath.Exists)
|
||||||
|
Directory.CreateDirectory(path: parentPath.FullName);
|
||||||
|
|
||||||
|
File.WriteAllText(path, contents: ToSvg());
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,136 +7,6 @@ using PSRule.Rules;
|
||||||
|
|
||||||
namespace PSRule.Badges;
|
namespace PSRule.Badges;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of badge.
|
|
||||||
/// </summary>
|
|
||||||
public enum BadgeType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A badge that reports an unknown state.
|
|
||||||
/// </summary>
|
|
||||||
Unknown = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A badge reporting a successful state.
|
|
||||||
/// </summary>
|
|
||||||
Success = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A bagde reporting a failed state.
|
|
||||||
/// </summary>
|
|
||||||
Failure = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An instance of a badge created by the badge API.
|
|
||||||
/// </summary>
|
|
||||||
public interface IBadge
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Get the badge as SVG text content.
|
|
||||||
/// </summary>
|
|
||||||
string ToSvg();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write the SVG badge content directly to disk.
|
|
||||||
/// </summary>
|
|
||||||
void ToFile(string path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A builder for the badge API.
|
|
||||||
/// </summary>
|
|
||||||
public interface IBadgeBuilder
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Create a badge for the worst case of an analyzed object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">A single result. The worst case for all records of an object is used for the badge.</param>
|
|
||||||
/// <returns>An instance of a badge.</returns>
|
|
||||||
IBadge Create(InvokeResult result);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a badge for the worst case of all analyzed objects.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">A enumeration of results. The worst case from all results is used for the badge.</param>
|
|
||||||
/// <returns>An instance of a badge.</returns>
|
|
||||||
IBadge Create(IEnumerable<InvokeResult> result);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a custom badge.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="title">The left badge text.</param>
|
|
||||||
/// <param name="type">Determines if the result is Unknown, Success, or Failure.</param>
|
|
||||||
/// <param name="label">The right badge text.</param>
|
|
||||||
/// <returns>An instance of a badge.</returns>
|
|
||||||
IBadge Create(string title, BadgeType type, string label);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An instance of a badge created by the Badge API.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class Badge : IBadge
|
|
||||||
{
|
|
||||||
private readonly string _LeftText;
|
|
||||||
private readonly string _RightText;
|
|
||||||
private readonly double _LeftWidth;
|
|
||||||
private readonly double _RightWidth;
|
|
||||||
private readonly int _MidPadding;
|
|
||||||
private readonly int _BorderPadding;
|
|
||||||
private readonly string _Fill;
|
|
||||||
|
|
||||||
internal Badge(string left, string right, string fill)
|
|
||||||
{
|
|
||||||
_LeftWidth = BadgeResources.Measure(left);
|
|
||||||
_RightWidth = BadgeResources.Measure(right);
|
|
||||||
|
|
||||||
_LeftText = left;
|
|
||||||
_RightText = right;
|
|
||||||
_MidPadding = 3;
|
|
||||||
_BorderPadding = 7;
|
|
||||||
_Fill = fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return ToSvg();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string ToSvg()
|
|
||||||
{
|
|
||||||
var w = (int)Math.Round(_LeftWidth + _RightWidth + 2 * _BorderPadding + 2 * _MidPadding);
|
|
||||||
var x = (int)Math.Round(_LeftWidth + _BorderPadding + _MidPadding);
|
|
||||||
|
|
||||||
var builder = new SvgBuilder(
|
|
||||||
width: w,
|
|
||||||
height: 20,
|
|
||||||
textScale: 10,
|
|
||||||
midPoint: x,
|
|
||||||
rounding: 2,
|
|
||||||
borderPadding: _BorderPadding,
|
|
||||||
midPadding: _MidPadding);
|
|
||||||
builder.Begin(string.Concat(_LeftText, ": ", _RightText));
|
|
||||||
builder.Backfill(_Fill);
|
|
||||||
builder.TextBlock(_LeftText, _RightText, 110);
|
|
||||||
builder.End();
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void ToFile(string path)
|
|
||||||
{
|
|
||||||
path = Environment.GetRootedPath(path);
|
|
||||||
var parentPath = Directory.GetParent(path);
|
|
||||||
if (!parentPath.Exists)
|
|
||||||
Directory.CreateDirectory(path: parentPath.FullName);
|
|
||||||
|
|
||||||
File.WriteAllText(path, contents: ToSvg());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A badge builder that implements the Badge API within PSRule.
|
/// A badge builder that implements the Badge API within PSRule.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Badges;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of badge.
|
||||||
|
/// </summary>
|
||||||
|
public enum BadgeType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A badge that reports an unknown state.
|
||||||
|
/// </summary>
|
||||||
|
Unknown = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A badge reporting a successful state.
|
||||||
|
/// </summary>
|
||||||
|
Success = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A badge reporting a failed state.
|
||||||
|
/// </summary>
|
||||||
|
Failure = 2
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Badges;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An instance of a badge created by the badge API.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBadge
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the badge as SVG text content.
|
||||||
|
/// </summary>
|
||||||
|
string ToSvg();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write the SVG badge content directly to disk.
|
||||||
|
/// </summary>
|
||||||
|
void ToFile(string path);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using PSRule.Pipeline;
|
||||||
|
|
||||||
|
namespace PSRule.Badges;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A builder for the badge API.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBadgeBuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a badge for the worst case of an analyzed object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">A single result. The worst case for all records of an object is used for the badge.</param>
|
||||||
|
/// <returns>An instance of a badge.</returns>
|
||||||
|
IBadge Create(InvokeResult result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a badge for the worst case of all analyzed objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">A enumeration of results. The worst case from all results is used for the badge.</param>
|
||||||
|
/// <returns>An instance of a badge.</returns>
|
||||||
|
IBadge Create(IEnumerable<InvokeResult> result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a custom badge.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The left badge text.</param>
|
||||||
|
/// <param name="type">Determines if the result is Unknown, Success, or Failure.</param>
|
||||||
|
/// <param name="label">The right badge text.</param>
|
||||||
|
/// <returns>An instance of a badge.</returns>
|
||||||
|
IBadge Create(string title, BadgeType type, string label);
|
||||||
|
}
|
|
@ -25,7 +25,8 @@ internal static class ResourceExtensions
|
||||||
|
|
||||||
internal static bool IsLocalScope(this IResource resource)
|
internal static bool IsLocalScope(this IResource resource)
|
||||||
{
|
{
|
||||||
return string.IsNullOrEmpty(resource.Source.Module);
|
return resource != null &&
|
||||||
|
(string.IsNullOrEmpty(resource.Id.Scope) || resource.Id.Scope == ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IEnumerable<ResourceId> GetIds(this IResource resource)
|
internal static IEnumerable<ResourceId> GetIds(this IResource resource)
|
||||||
|
|
|
@ -2,19 +2,9 @@
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace PSRule.Configuration;
|
namespace PSRule.Configuration;
|
||||||
|
|
||||||
internal static class BindingOptionExtensions
|
|
||||||
{
|
|
||||||
[DebuggerStepThrough]
|
|
||||||
public static StringComparer GetComparer(this BindingOption option)
|
|
||||||
{
|
|
||||||
return option.IgnoreCase.GetValueOrDefault(BindingOption.Default.IgnoreCase.Value) ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Options that affect property binding of TargetName and TargetType.
|
/// Options that affect property binding of TargetName and TargetType.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace PSRule.Configuration;
|
||||||
|
|
||||||
|
internal static class BindingOptionExtensions
|
||||||
|
{
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public static StringComparer GetComparer(this BindingOption option)
|
||||||
|
{
|
||||||
|
return option.IgnoreCase.GetValueOrDefault(BindingOption.Default.IgnoreCase.Value) ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A resource language block.
|
||||||
|
/// </summary>
|
||||||
|
public interface IResource : ILanguageBlock
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type of resource.
|
||||||
|
/// </summary>
|
||||||
|
ResourceKind Kind { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The API version of the resource.
|
||||||
|
/// </summary>
|
||||||
|
string ApiVersion { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the resource.
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An optional reference identifer for the resource.
|
||||||
|
/// </summary>
|
||||||
|
ResourceId? Ref { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any additional aliases for the resource.
|
||||||
|
/// </summary>
|
||||||
|
ResourceId[] Alias { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any resource tags.
|
||||||
|
/// </summary>
|
||||||
|
ResourceTags Tags { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any taxonomy references.
|
||||||
|
/// </summary>
|
||||||
|
ResourceLabels Labels { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flags for the resource.
|
||||||
|
/// </summary>
|
||||||
|
ResourceFlags Flags { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The source location of the resource.
|
||||||
|
/// </summary>
|
||||||
|
ISourceExtent Extent { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional information about the resource.
|
||||||
|
/// </summary>
|
||||||
|
IResourceHelpInfo Info { get; }
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
internal interface IResourceVisitor
|
||||||
|
{
|
||||||
|
bool Visit(IResource resource);
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using PSRule.Pipeline;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A base class for built-in resource types.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSpec">The type of the related <seealso cref="Spec"/> for the resource.</typeparam>
|
||||||
|
public abstract class InternalResource<TSpec> : Resource<TSpec>, IResource, IAnnotated<ResourceAnnotation> where TSpec : Spec, new()
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Type, ResourceAnnotation> _Annotations;
|
||||||
|
|
||||||
|
private protected InternalResource(ResourceKind kind, string apiVersion, SourceFile source, ResourceMetadata metadata, IResourceHelpInfo info, ISourceExtent extent, TSpec spec)
|
||||||
|
: base(kind, apiVersion, source, metadata, info, extent, spec)
|
||||||
|
{
|
||||||
|
_Annotations = new Dictionary<Type, ResourceAnnotation>();
|
||||||
|
Obsolete = ResourceHelper.IsObsolete(metadata);
|
||||||
|
Flags |= ResourceHelper.IsObsolete(metadata) ? ResourceFlags.Obsolete : ResourceFlags.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlIgnore()]
|
||||||
|
internal readonly bool Obsolete;
|
||||||
|
|
||||||
|
[YamlIgnore()]
|
||||||
|
internal ResourceFlags Flags { get; }
|
||||||
|
|
||||||
|
ResourceKind IResource.Kind => Kind;
|
||||||
|
|
||||||
|
string IResource.ApiVersion => ApiVersion;
|
||||||
|
|
||||||
|
string IResource.Name => Name;
|
||||||
|
|
||||||
|
// Not supported with base resources.
|
||||||
|
ResourceId? IResource.Ref => null;
|
||||||
|
|
||||||
|
// Not supported with base resources.
|
||||||
|
ResourceId[] IResource.Alias => null;
|
||||||
|
|
||||||
|
ResourceTags IResource.Tags => Metadata.Tags;
|
||||||
|
|
||||||
|
ResourceLabels IResource.Labels => Metadata.Labels;
|
||||||
|
|
||||||
|
ResourceFlags IResource.Flags => Flags;
|
||||||
|
|
||||||
|
TAnnotation IAnnotated<ResourceAnnotation>.GetAnnotation<TAnnotation>()
|
||||||
|
{
|
||||||
|
return _Annotations.TryGetValue(typeof(TAnnotation), out var annotation) ? (TAnnotation)annotation : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAnnotated<ResourceAnnotation>.SetAnnotation<TAnnotation>(TAnnotation annotation)
|
||||||
|
{
|
||||||
|
_Annotations[typeof(TAnnotation)] = annotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,477 +1,12 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
using System.Collections;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
|
||||||
using PSRule.Converters.Yaml;
|
|
||||||
using PSRule.Definitions.Rules;
|
|
||||||
using PSRule.Pipeline;
|
using PSRule.Pipeline;
|
||||||
using PSRule.Runtime;
|
|
||||||
using YamlDotNet.Core;
|
|
||||||
using YamlDotNet.Core.Events;
|
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
|
||||||
using YamlDotNet.Serialization.NodeDeserializers;
|
|
||||||
|
|
||||||
namespace PSRule.Definitions;
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of resource.
|
|
||||||
/// </summary>
|
|
||||||
public enum ResourceKind
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Unknown or empty.
|
|
||||||
/// </summary>
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A rule resource.
|
|
||||||
/// </summary>
|
|
||||||
Rule = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A baseline resource.
|
|
||||||
/// </summary>
|
|
||||||
Baseline = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A module configuration resource.
|
|
||||||
/// </summary>
|
|
||||||
ModuleConfig = 3,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A selector resource.
|
|
||||||
/// </summary>
|
|
||||||
Selector = 4,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A convention.
|
|
||||||
/// </summary>
|
|
||||||
Convention = 5,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A suppression group.
|
|
||||||
/// </summary>
|
|
||||||
SuppressionGroup = 6
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional flags that indicate the status of the resource.
|
|
||||||
/// </summary>
|
|
||||||
[Flags]
|
|
||||||
public enum ResourceFlags
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// No flags are set.
|
|
||||||
/// </summary>
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The resource is obsolete.
|
|
||||||
/// </summary>
|
|
||||||
Obsolete = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A resource langange block.
|
|
||||||
/// </summary>
|
|
||||||
public interface IResource : ILanguageBlock
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The type of resource.
|
|
||||||
/// </summary>
|
|
||||||
ResourceKind Kind { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The API version of the resource.
|
|
||||||
/// </summary>
|
|
||||||
string ApiVersion { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the resource.
|
|
||||||
/// </summary>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An optional reference identifer for the resource.
|
|
||||||
/// </summary>
|
|
||||||
ResourceId? Ref { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any additional aliases for the resource.
|
|
||||||
/// </summary>
|
|
||||||
ResourceId[] Alias { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any resource tags.
|
|
||||||
/// </summary>
|
|
||||||
ResourceTags Tags { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any taxonomy references.
|
|
||||||
/// </summary>
|
|
||||||
ResourceLabels Labels { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Flags for the resource.
|
|
||||||
/// </summary>
|
|
||||||
ResourceFlags Flags { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The source location of the resource.
|
|
||||||
/// </summary>
|
|
||||||
ISourceExtent Extent { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional information about the resource.
|
|
||||||
/// </summary>
|
|
||||||
IResourceHelpInfo Info { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface IResourceVisitor
|
|
||||||
{
|
|
||||||
bool Visit(IResource resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal abstract class ResourceRef
|
|
||||||
{
|
|
||||||
public readonly string Id;
|
|
||||||
public readonly ResourceKind Kind;
|
|
||||||
|
|
||||||
protected ResourceRef(string id, ResourceKind kind)
|
|
||||||
{
|
|
||||||
Kind = kind;
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A base resource annotation.
|
|
||||||
/// </summary>
|
|
||||||
internal abstract class ResourceAnnotation
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Annotation used to flag validation issues.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class ValidateResourceAnnotation : ResourceAnnotation
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A resource object.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ResourceObject
|
|
||||||
{
|
|
||||||
internal ResourceObject(IResource block)
|
|
||||||
{
|
|
||||||
Block = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IResource Block { get; }
|
|
||||||
|
|
||||||
internal bool Visit(IResourceVisitor visitor)
|
|
||||||
{
|
|
||||||
return Block != null && visitor != null && visitor.Visit(Block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class ResourceBuilder
|
|
||||||
{
|
|
||||||
private readonly List<ILanguageBlock> _Output;
|
|
||||||
private readonly IDeserializer _Deserializer;
|
|
||||||
|
|
||||||
internal ResourceBuilder()
|
|
||||||
{
|
|
||||||
_Output = new List<ILanguageBlock>();
|
|
||||||
_Deserializer = new DeserializerBuilder()
|
|
||||||
.IgnoreUnmatchedProperties()
|
|
||||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
|
||||||
.WithTypeConverter(new FieldMapYamlTypeConverter())
|
|
||||||
.WithTypeConverter(new StringArrayMapConverter())
|
|
||||||
.WithTypeConverter(new StringArrayConverter())
|
|
||||||
.WithNodeDeserializer(
|
|
||||||
inner => new ResourceNodeDeserializer(new LanguageExpressionDeserializer(inner)),
|
|
||||||
s => s.InsteadOf<ObjectNodeDeserializer>())
|
|
||||||
.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void FromFile(SourceFile file)
|
|
||||||
{
|
|
||||||
using var reader = new StreamReader(file.Path);
|
|
||||||
var parser = new YamlDotNet.Core.Parser(reader);
|
|
||||||
parser.TryConsume<StreamStart>(out _);
|
|
||||||
while (parser.Current is DocumentStart)
|
|
||||||
{
|
|
||||||
var item = _Deserializer.Deserialize<ResourceObject>(parser: parser);
|
|
||||||
if (item == null || item.Block == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_Output.Add(item.Block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IEnumerable<ILanguageBlock> Build()
|
|
||||||
{
|
|
||||||
return _Output.Count == 0 ? Array.Empty<ILanguageBlock>() : _Output.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional resource annotations.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ResourceAnnotations : Dictionary<string, object>
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional resource taxonomy references.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ResourceLabels : Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Create an empty set of resource labels.
|
|
||||||
/// </summary>
|
|
||||||
public ResourceLabels() : base(StringComparer.OrdinalIgnoreCase) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert from a hashtable to resource labels.
|
|
||||||
/// </summary>
|
|
||||||
internal static ResourceLabels FromHashtable(Hashtable hashtable)
|
|
||||||
{
|
|
||||||
if (hashtable == null || hashtable.Count == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var annotations = new ResourceLabels();
|
|
||||||
foreach (DictionaryEntry kv in hashtable)
|
|
||||||
{
|
|
||||||
var key = kv.Key.ToString();
|
|
||||||
if (hashtable.TryGetStringArray(key, out var value))
|
|
||||||
annotations[key] = value;
|
|
||||||
}
|
|
||||||
return annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool Contains(string key, string[] value)
|
|
||||||
{
|
|
||||||
if (!TryGetValue(key, out var actual))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (value == null || value.Length == 0 || (value.Length == 1 && value[0] == "*"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (var i = 0; i < value.Length; i++)
|
|
||||||
{
|
|
||||||
if (Array.IndexOf(actual, value[i]) != -1)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional resource tags.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ResourceTags : Dictionary<string, string>
|
|
||||||
{
|
|
||||||
private Hashtable _Hashtable;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create an empty set of resource tags.
|
|
||||||
/// </summary>
|
|
||||||
public ResourceTags() : base(StringComparer.OrdinalIgnoreCase) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert from a hashtable to resource tags.
|
|
||||||
/// </summary>
|
|
||||||
internal static ResourceTags FromHashtable(Hashtable hashtable)
|
|
||||||
{
|
|
||||||
if (hashtable == null || hashtable.Count == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var tags = new ResourceTags();
|
|
||||||
foreach (DictionaryEntry kv in hashtable)
|
|
||||||
tags[kv.Key.ToString()] = kv.Value.ToString();
|
|
||||||
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert from a dictionary of string pairs to resource tags.
|
|
||||||
/// </summary>
|
|
||||||
internal static ResourceTags FromDictionary(Dictionary<string, string> dictionary)
|
|
||||||
{
|
|
||||||
if (dictionary == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var tags = new ResourceTags();
|
|
||||||
foreach (var kv in dictionary)
|
|
||||||
tags[kv.Key] = kv.Value;
|
|
||||||
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert resource tags to a hashtable.
|
|
||||||
/// </summary>
|
|
||||||
public Hashtable ToHashtable()
|
|
||||||
{
|
|
||||||
_Hashtable ??= new ReadOnlyHashtable(this, StringComparer.OrdinalIgnoreCase);
|
|
||||||
return _Hashtable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if a specific resource tag exists.
|
|
||||||
/// </summary>
|
|
||||||
internal bool Contains(object key, object value)
|
|
||||||
{
|
|
||||||
if (key == null || value == null || key is not string k || !ContainsKey(k))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (TryArray(value, out var values))
|
|
||||||
{
|
|
||||||
for (var i = 0; i < values.Length; i++)
|
|
||||||
{
|
|
||||||
if (Comparer.Equals(values[i], this[k]))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var v = value.ToString();
|
|
||||||
return v == "*" || Comparer.Equals(v, this[k]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryArray(object o, out string[] values)
|
|
||||||
{
|
|
||||||
values = null;
|
|
||||||
if (o is string[] sArray)
|
|
||||||
{
|
|
||||||
values = sArray;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o is IEnumerable<object> oValues)
|
|
||||||
{
|
|
||||||
var result = new List<string>();
|
|
||||||
foreach (var obj in oValues)
|
|
||||||
result.Add(obj.ToString());
|
|
||||||
|
|
||||||
values = result.ToArray();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert the resourecs tags to a display string for PowerShell views.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public string ToViewString()
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
foreach (var kv in this)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
sb.Append(System.Environment.NewLine);
|
|
||||||
|
|
||||||
sb.Append(kv.Key);
|
|
||||||
sb.Append('=');
|
|
||||||
sb.Append('\'');
|
|
||||||
sb.Append(kv.Value);
|
|
||||||
sb.Append('\'');
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional resource metadata.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ResourceMetadata
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Create an empty set of metadata.
|
|
||||||
/// </summary>
|
|
||||||
public ResourceMetadata()
|
|
||||||
{
|
|
||||||
Annotations = new ResourceAnnotations();
|
|
||||||
Tags = new ResourceTags();
|
|
||||||
Labels = new ResourceLabels();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the resource.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A non-localized display name for the resource.
|
|
||||||
/// </summary>
|
|
||||||
public string DisplayName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A non-localized description of the resource.
|
|
||||||
/// </summary>
|
|
||||||
public string Description { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A opaque reference for the resource.
|
|
||||||
/// </summary>
|
|
||||||
public string Ref { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional aliases for the resource.
|
|
||||||
/// </summary>
|
|
||||||
public string[] Alias { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any resource annotations.
|
|
||||||
/// </summary>
|
|
||||||
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
|
||||||
public ResourceAnnotations Annotations { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any resource tags.
|
|
||||||
/// </summary>
|
|
||||||
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
|
||||||
public ResourceTags Tags { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Any taxonomy references.
|
|
||||||
/// </summary>
|
|
||||||
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
|
||||||
public ResourceLabels Labels { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A URL to documentation for the resource.
|
|
||||||
/// </summary>
|
|
||||||
public string Link { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The source location of the resource.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ResourceExtent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The file where the resource is located.
|
|
||||||
/// </summary>
|
|
||||||
public string File { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the module if the resource is contained within a module.
|
|
||||||
/// </summary>
|
|
||||||
public string Module { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A base class for resources.
|
/// A base class for resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -550,132 +85,3 @@ public abstract class Resource<TSpec> where TSpec : Spec, new()
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ISourceExtent Extent { get; }
|
public ISourceExtent Extent { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A base class for built-in resource types.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSpec">The type of the related <seealso cref="Spec"/> for the resource.</typeparam>
|
|
||||||
public abstract class InternalResource<TSpec> : Resource<TSpec>, IResource, IAnnotated<ResourceAnnotation> where TSpec : Spec, new()
|
|
||||||
{
|
|
||||||
private readonly Dictionary<Type, ResourceAnnotation> _Annotations;
|
|
||||||
|
|
||||||
private protected InternalResource(ResourceKind kind, string apiVersion, SourceFile source, ResourceMetadata metadata, IResourceHelpInfo info, ISourceExtent extent, TSpec spec)
|
|
||||||
: base(kind, apiVersion, source, metadata, info, extent, spec)
|
|
||||||
{
|
|
||||||
_Annotations = new Dictionary<Type, ResourceAnnotation>();
|
|
||||||
Obsolete = ResourceHelper.IsObsolete(metadata);
|
|
||||||
Flags |= ResourceHelper.IsObsolete(metadata) ? ResourceFlags.Obsolete : ResourceFlags.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
[YamlIgnore()]
|
|
||||||
internal readonly bool Obsolete;
|
|
||||||
|
|
||||||
[YamlIgnore()]
|
|
||||||
internal ResourceFlags Flags { get; }
|
|
||||||
|
|
||||||
ResourceKind IResource.Kind => Kind;
|
|
||||||
|
|
||||||
string IResource.ApiVersion => ApiVersion;
|
|
||||||
|
|
||||||
string IResource.Name => Name;
|
|
||||||
|
|
||||||
// Not supported with base resources.
|
|
||||||
ResourceId? IResource.Ref => null;
|
|
||||||
|
|
||||||
// Not supported with base resources.
|
|
||||||
ResourceId[] IResource.Alias => null;
|
|
||||||
|
|
||||||
ResourceTags IResource.Tags => Metadata.Tags;
|
|
||||||
|
|
||||||
ResourceLabels IResource.Labels => Metadata.Labels;
|
|
||||||
|
|
||||||
ResourceFlags IResource.Flags => Flags;
|
|
||||||
|
|
||||||
TAnnotation IAnnotated<ResourceAnnotation>.GetAnnotation<TAnnotation>()
|
|
||||||
{
|
|
||||||
return _Annotations.TryGetValue(typeof(TAnnotation), out var annotation) ? (TAnnotation)annotation : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IAnnotated<ResourceAnnotation>.SetAnnotation<TAnnotation>(TAnnotation annotation)
|
|
||||||
{
|
|
||||||
_Annotations[typeof(TAnnotation)] = annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class ResourceHelper
|
|
||||||
{
|
|
||||||
private const string ANNOTATION_OBSOLETE = "obsolete";
|
|
||||||
|
|
||||||
private const char SCOPE_SEPARATOR = '\\';
|
|
||||||
|
|
||||||
internal static string GetIdString(string scope, string name)
|
|
||||||
{
|
|
||||||
return name.IndexOf(SCOPE_SEPARATOR) >= 0
|
|
||||||
? name
|
|
||||||
: string.Concat(
|
|
||||||
LanguageScope.Normalize(scope),
|
|
||||||
SCOPE_SEPARATOR,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ParseIdString(string defaultScope, string id, out string scope, out string name)
|
|
||||||
{
|
|
||||||
ParseIdString(id, out scope, out name);
|
|
||||||
scope ??= LanguageScope.Normalize(defaultScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ParseIdString(string id, out string scope, out string name)
|
|
||||||
{
|
|
||||||
scope = null;
|
|
||||||
name = null;
|
|
||||||
if (string.IsNullOrEmpty(id))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var scopeSeparator = id.IndexOf(SCOPE_SEPARATOR);
|
|
||||||
scope = scopeSeparator >= 0 ? id.Substring(0, scopeSeparator) : null;
|
|
||||||
name = id.Substring(scopeSeparator + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks each resource name and converts each into a full qualified <seealso cref="ResourceId"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="defaultScope">The default scope to use if the resource name if not fully qualified.</param>
|
|
||||||
/// <param name="name">An array of names. Qualified names (RuleIds) supplied are left intact.</param>
|
|
||||||
/// <param name="kind">The <seealso cref="ResourceIdKind"/> of the <seealso cref="ResourceId"/>.</param>
|
|
||||||
/// <returns>An array of RuleIds.</returns>
|
|
||||||
internal static ResourceId[] GetRuleId(string defaultScope, string[] name, ResourceIdKind kind)
|
|
||||||
{
|
|
||||||
if (name == null || name.Length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var result = new ResourceId[name.Length];
|
|
||||||
for (var i = 0; i < name.Length; i++)
|
|
||||||
result[i] = GetRuleId(defaultScope, name[i], kind);
|
|
||||||
|
|
||||||
return (result.Length == 0) ? null : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ResourceId GetRuleId(string defaultScope, string name, ResourceIdKind kind)
|
|
||||||
{
|
|
||||||
return name.IndexOf(SCOPE_SEPARATOR) > 0 ? ResourceId.Parse(name, kind) : new ResourceId(defaultScope, name, kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ResourceId? GetIdNullable(string scope, string name, ResourceIdKind kind)
|
|
||||||
{
|
|
||||||
return string.IsNullOrEmpty(name) ? null : new ResourceId(scope, name, kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsObsolete(ResourceMetadata metadata)
|
|
||||||
{
|
|
||||||
return metadata != null &&
|
|
||||||
metadata.Annotations != null &&
|
|
||||||
metadata.Annotations.TryGetBool(ANNOTATION_OBSOLETE, out var obsolete)
|
|
||||||
&& obsolete.GetValueOrDefault(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static SeverityLevel GetLevel(SeverityLevel? level)
|
|
||||||
{
|
|
||||||
return !level.HasValue || level.Value == SeverityLevel.None ? RuleV1.DEFAULT_LEVEL : level.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A base resource annotation.
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class ResourceAnnotation
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional resource annotations.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ResourceAnnotations : Dictionary<string, object>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using PSRule.Converters.Yaml;
|
||||||
|
using PSRule.Pipeline;
|
||||||
|
using YamlDotNet.Core;
|
||||||
|
using YamlDotNet.Core.Events;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
|
using YamlDotNet.Serialization.NodeDeserializers;
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
internal sealed class ResourceBuilder
|
||||||
|
{
|
||||||
|
private readonly List<ILanguageBlock> _Output;
|
||||||
|
private readonly IDeserializer _Deserializer;
|
||||||
|
|
||||||
|
internal ResourceBuilder()
|
||||||
|
{
|
||||||
|
_Output = new List<ILanguageBlock>();
|
||||||
|
_Deserializer = new DeserializerBuilder()
|
||||||
|
.IgnoreUnmatchedProperties()
|
||||||
|
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||||
|
.WithTypeConverter(new FieldMapYamlTypeConverter())
|
||||||
|
.WithTypeConverter(new StringArrayMapConverter())
|
||||||
|
.WithTypeConverter(new StringArrayConverter())
|
||||||
|
.WithNodeDeserializer(
|
||||||
|
inner => new ResourceNodeDeserializer(new LanguageExpressionDeserializer(inner)),
|
||||||
|
s => s.InsteadOf<ObjectNodeDeserializer>())
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void FromFile(SourceFile file)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(file.Path);
|
||||||
|
var parser = new YamlDotNet.Core.Parser(reader);
|
||||||
|
parser.TryConsume<StreamStart>(out _);
|
||||||
|
while (parser.Current is DocumentStart)
|
||||||
|
{
|
||||||
|
var item = _Deserializer.Deserialize<ResourceObject>(parser: parser);
|
||||||
|
if (item == null || item.Block == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_Output.Add(item.Block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IEnumerable<ILanguageBlock> Build()
|
||||||
|
{
|
||||||
|
return _Output.Count == 0 ? Array.Empty<ILanguageBlock>() : _Output.ToArray();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The source location of the resource.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ResourceExtent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The file where the resource is located.
|
||||||
|
/// </summary>
|
||||||
|
public string File { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the module if the resource is contained within a module.
|
||||||
|
/// </summary>
|
||||||
|
public string Module { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional flags that indicate the status of the resource.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum ResourceFlags
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No flags are set.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The resource is obsolete.
|
||||||
|
/// </summary>
|
||||||
|
Obsolete = 1
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using PSRule.Definitions.Rules;
|
||||||
|
using PSRule.Runtime;
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
internal static class ResourceHelper
|
||||||
|
{
|
||||||
|
private const string ANNOTATION_OBSOLETE = "obsolete";
|
||||||
|
|
||||||
|
private const char SCOPE_SEPARATOR = '\\';
|
||||||
|
|
||||||
|
internal static string GetIdString(string scope, string name)
|
||||||
|
{
|
||||||
|
return name.IndexOf(SCOPE_SEPARATOR) >= 0
|
||||||
|
? name
|
||||||
|
: string.Concat(
|
||||||
|
LanguageScope.Normalize(scope),
|
||||||
|
SCOPE_SEPARATOR,
|
||||||
|
name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ParseIdString(string defaultScope, string id, out string scope, out string name)
|
||||||
|
{
|
||||||
|
ParseIdString(id, out scope, out name);
|
||||||
|
scope ??= LanguageScope.Normalize(defaultScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ParseIdString(string id, out string scope, out string name)
|
||||||
|
{
|
||||||
|
scope = null;
|
||||||
|
name = null;
|
||||||
|
if (string.IsNullOrEmpty(id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var scopeSeparator = id.IndexOf(SCOPE_SEPARATOR);
|
||||||
|
scope = scopeSeparator >= 0 ? id.Substring(0, scopeSeparator) : null;
|
||||||
|
name = id.Substring(scopeSeparator + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks each resource name and converts each into a full qualified <seealso cref="ResourceId"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultScope">The default scope to use if the resource name if not fully qualified.</param>
|
||||||
|
/// <param name="name">An array of names. Qualified names (RuleIds) supplied are left intact.</param>
|
||||||
|
/// <param name="kind">The <seealso cref="ResourceIdKind"/> of the <seealso cref="ResourceId"/>.</param>
|
||||||
|
/// <returns>An array of RuleIds.</returns>
|
||||||
|
internal static ResourceId[] GetRuleId(string defaultScope, string[] name, ResourceIdKind kind)
|
||||||
|
{
|
||||||
|
if (name == null || name.Length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var result = new ResourceId[name.Length];
|
||||||
|
for (var i = 0; i < name.Length; i++)
|
||||||
|
result[i] = GetRuleId(defaultScope, name[i], kind);
|
||||||
|
|
||||||
|
return (result.Length == 0) ? null : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ResourceId GetRuleId(string defaultScope, string name, ResourceIdKind kind)
|
||||||
|
{
|
||||||
|
return name.IndexOf(SCOPE_SEPARATOR) > 0 ? ResourceId.Parse(name, kind) : new ResourceId(defaultScope, name, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ResourceId? GetIdNullable(string scope, string name, ResourceIdKind kind)
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(name) ? null : new ResourceId(scope, name, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsObsolete(ResourceMetadata metadata)
|
||||||
|
{
|
||||||
|
return metadata != null &&
|
||||||
|
metadata.Annotations != null &&
|
||||||
|
metadata.Annotations.TryGetBool(ANNOTATION_OBSOLETE, out var obsolete)
|
||||||
|
&& obsolete.GetValueOrDefault(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SeverityLevel GetLevel(SeverityLevel? level)
|
||||||
|
{
|
||||||
|
return !level.HasValue || level.Value == SeverityLevel.None ? RuleV1.DEFAULT_LEVEL : level.Value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of resource.
|
||||||
|
/// </summary>
|
||||||
|
public enum ResourceKind
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unknown or empty.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A rule resource.
|
||||||
|
/// </summary>
|
||||||
|
Rule = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A baseline resource.
|
||||||
|
/// </summary>
|
||||||
|
Baseline = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A module configuration resource.
|
||||||
|
/// </summary>
|
||||||
|
ModuleConfig = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A selector resource.
|
||||||
|
/// </summary>
|
||||||
|
Selector = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A convention.
|
||||||
|
/// </summary>
|
||||||
|
Convention = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A suppression group.
|
||||||
|
/// </summary>
|
||||||
|
SuppressionGroup = 6
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional resource taxonomy references.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ResourceLabels : Dictionary<string, string[]>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create an empty set of resource labels.
|
||||||
|
/// </summary>
|
||||||
|
public ResourceLabels() : base(StringComparer.OrdinalIgnoreCase) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert from a hashtable to resource labels.
|
||||||
|
/// </summary>
|
||||||
|
internal static ResourceLabels FromHashtable(Hashtable hashtable)
|
||||||
|
{
|
||||||
|
if (hashtable == null || hashtable.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var annotations = new ResourceLabels();
|
||||||
|
foreach (DictionaryEntry kv in hashtable)
|
||||||
|
{
|
||||||
|
var key = kv.Key.ToString();
|
||||||
|
if (hashtable.TryGetStringArray(key, out var value))
|
||||||
|
annotations[key] = value;
|
||||||
|
}
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool Contains(string key, string[] value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var actual))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (value == null || value.Length == 0 || (value.Length == 1 && value[0] == "*"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (var i = 0; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
if (Array.IndexOf(actual, value[i]) != -1)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional resource metadata.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ResourceMetadata
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create an empty set of metadata.
|
||||||
|
/// </summary>
|
||||||
|
public ResourceMetadata()
|
||||||
|
{
|
||||||
|
Annotations = new ResourceAnnotations();
|
||||||
|
Tags = new ResourceTags();
|
||||||
|
Labels = new ResourceLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the resource.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A non-localized display name for the resource.
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A non-localized description of the resource.
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A opaque reference for the resource.
|
||||||
|
/// </summary>
|
||||||
|
public string Ref { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional aliases for the resource.
|
||||||
|
/// </summary>
|
||||||
|
public string[] Alias { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any resource annotations.
|
||||||
|
/// </summary>
|
||||||
|
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
||||||
|
public ResourceAnnotations Annotations { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any resource tags.
|
||||||
|
/// </summary>
|
||||||
|
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
||||||
|
public ResourceTags Tags { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any taxonomy references.
|
||||||
|
/// </summary>
|
||||||
|
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
||||||
|
public ResourceLabels Labels { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A URL to documentation for the resource.
|
||||||
|
/// </summary>
|
||||||
|
public string Link { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A resource object.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ResourceObject
|
||||||
|
{
|
||||||
|
internal ResourceObject(IResource block)
|
||||||
|
{
|
||||||
|
Block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IResource Block { get; }
|
||||||
|
|
||||||
|
internal bool Visit(IResourceVisitor visitor)
|
||||||
|
{
|
||||||
|
return Block != null && visitor != null && visitor.Visit(Block);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
internal abstract class ResourceRef
|
||||||
|
{
|
||||||
|
public readonly string Id;
|
||||||
|
public readonly ResourceKind Kind;
|
||||||
|
|
||||||
|
protected ResourceRef(string id, ResourceKind kind)
|
||||||
|
{
|
||||||
|
Kind = kind;
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional resource tags.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ResourceTags : Dictionary<string, string>
|
||||||
|
{
|
||||||
|
private Hashtable _Hashtable;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an empty set of resource tags.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public ResourceTags() : base(StringComparer.OrdinalIgnoreCase) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert from a hashtable to resource tags.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
internal static ResourceTags FromHashtable(Hashtable hashtable)
|
||||||
|
{
|
||||||
|
if (hashtable == null || hashtable.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tags = new ResourceTags();
|
||||||
|
foreach (DictionaryEntry kv in hashtable)
|
||||||
|
tags[kv.Key.ToString()] = kv.Value.ToString();
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert from a dictionary of string pairs to resource tags.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
internal static ResourceTags FromDictionary(Dictionary<string, string> dictionary)
|
||||||
|
{
|
||||||
|
if (dictionary == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tags = new ResourceTags();
|
||||||
|
foreach (var kv in dictionary)
|
||||||
|
tags[kv.Key] = kv.Value;
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert resource tags to a hashtable.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public Hashtable ToHashtable()
|
||||||
|
{
|
||||||
|
_Hashtable ??= new ReadOnlyHashtable(this, StringComparer.OrdinalIgnoreCase);
|
||||||
|
return _Hashtable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a specific resource tag exists.
|
||||||
|
/// </summary>
|
||||||
|
internal bool Contains(object key, object value)
|
||||||
|
{
|
||||||
|
if (key == null || value == null || key is not string k || !ContainsKey(k))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (TryArray(value, out var values))
|
||||||
|
{
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (Comparer.Equals(values[i], this[k]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var v = value.ToString();
|
||||||
|
return v == "*" || Comparer.Equals(v, this[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryArray(object o, out string[] values)
|
||||||
|
{
|
||||||
|
values = null;
|
||||||
|
if (o is string[] sArray)
|
||||||
|
{
|
||||||
|
values = sArray;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o is IEnumerable<object> oValues)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
foreach (var obj in oValues)
|
||||||
|
result.Add(obj.ToString());
|
||||||
|
|
||||||
|
values = result.ToArray();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the resourecs tags to a display string for PowerShell views.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string ToViewString()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
sb.Append(System.Environment.NewLine);
|
||||||
|
|
||||||
|
sb.Append(kv.Key);
|
||||||
|
sb.Append('=');
|
||||||
|
sb.Append('\'');
|
||||||
|
sb.Append(kv.Value);
|
||||||
|
sb.Append('\'');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,12 +48,6 @@ internal sealed class RuleFilter : IResourceFilter
|
||||||
|
|
||||||
ResourceKind IResourceFilter.Kind => ResourceKind.Rule;
|
ResourceKind IResourceFilter.Kind => ResourceKind.Rule;
|
||||||
|
|
||||||
internal bool Match(string name, ResourceTags tag, ResourceLabels labels)
|
|
||||||
{
|
|
||||||
return !IsExcluded(new ResourceId[] { ResourceId.Parse(name) }) &&
|
|
||||||
IsIncluded(new ResourceId[] { ResourceId.Parse(name) }, tag, labels);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Matches if the RuleId is contained or any tag is matched
|
/// Matches if the RuleId is contained or any tag is matched
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Definitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Annotation used to flag validation issues.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class ValidateResourceAnnotation : ResourceAnnotation
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -3,13 +3,6 @@
|
||||||
|
|
||||||
namespace PSRule.Help;
|
namespace PSRule.Help;
|
||||||
|
|
||||||
internal enum MarkdownReaderMode
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
|
|
||||||
List
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stateful markdown reader.
|
/// Stateful markdown reader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Help;
|
||||||
|
|
||||||
|
internal enum MarkdownReaderMode
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
|
||||||
|
List
|
||||||
|
}
|
|
@ -7,49 +7,6 @@ namespace PSRule.Help;
|
||||||
|
|
||||||
internal delegate bool CharacterMatchDelegate(char c);
|
internal delegate bool CharacterMatchDelegate(char c);
|
||||||
|
|
||||||
[DebuggerDisplay("StartPos = (L: {Start}, C: {Column}), EndPos = (L: {End}, C: {Column.End}), Text = {Text}")]
|
|
||||||
internal sealed class SourceExtent
|
|
||||||
{
|
|
||||||
private readonly string _Source;
|
|
||||||
|
|
||||||
// Lazily cache extracted text
|
|
||||||
private string _Text;
|
|
||||||
|
|
||||||
internal SourceExtent(string source, string path, int start, int end, int line, int column)
|
|
||||||
{
|
|
||||||
_Text = null;
|
|
||||||
_Source = source;
|
|
||||||
Path = path;
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
Line = line;
|
|
||||||
Column = column;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly string Path;
|
|
||||||
|
|
||||||
public readonly int Start;
|
|
||||||
|
|
||||||
public readonly int End;
|
|
||||||
|
|
||||||
public readonly int Line;
|
|
||||||
|
|
||||||
public readonly int Column;
|
|
||||||
|
|
||||||
public string Text
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_Text == null)
|
|
||||||
{
|
|
||||||
_Text = _Source.Substring(Start, (End - Start));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerDisplay("Position = {Position}, Current = {Current}")]
|
[DebuggerDisplay("Position = {Position}, Current = {Current}")]
|
||||||
internal sealed class MarkdownStream
|
internal sealed class MarkdownStream
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,70 +5,6 @@ using System.Diagnostics;
|
||||||
|
|
||||||
namespace PSRule.Help;
|
namespace PSRule.Help;
|
||||||
|
|
||||||
internal enum MarkdownTokenType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
Text,
|
|
||||||
|
|
||||||
Header,
|
|
||||||
|
|
||||||
FencedBlock,
|
|
||||||
|
|
||||||
LineBreak,
|
|
||||||
|
|
||||||
ParagraphStart,
|
|
||||||
|
|
||||||
ParagraphEnd,
|
|
||||||
|
|
||||||
LinkReference,
|
|
||||||
|
|
||||||
Link,
|
|
||||||
|
|
||||||
LinkReferenceDefinition,
|
|
||||||
|
|
||||||
YamlKeyValue
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags()]
|
|
||||||
internal enum MarkdownTokens
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
Italic = 1,
|
|
||||||
|
|
||||||
Bold = 2,
|
|
||||||
|
|
||||||
Code = 4,
|
|
||||||
|
|
||||||
LineEnding = 8,
|
|
||||||
|
|
||||||
LineBreak = 16,
|
|
||||||
|
|
||||||
Preserve = 32,
|
|
||||||
|
|
||||||
// Accelerators
|
|
||||||
PreserveLineEnding = 40
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class MarkdownTokenFlagExtensions
|
|
||||||
{
|
|
||||||
public static bool IsEnding(this MarkdownTokens flags)
|
|
||||||
{
|
|
||||||
return flags.HasFlag(MarkdownTokens.LineEnding) || flags.HasFlag(MarkdownTokens.LineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsLineBreak(this MarkdownTokens flags)
|
|
||||||
{
|
|
||||||
return flags.HasFlag(MarkdownTokens.LineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ShouldPreserve(this MarkdownTokens flags)
|
|
||||||
{
|
|
||||||
return flags.HasFlag(MarkdownTokens.Preserve);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerDisplay("Type = {Type}, Text = {Text}")]
|
[DebuggerDisplay("Type = {Type}, Text = {Text}")]
|
||||||
internal sealed class MarkdownToken
|
internal sealed class MarkdownToken
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Help;
|
||||||
|
|
||||||
|
internal static class MarkdownTokenFlagExtensions
|
||||||
|
{
|
||||||
|
public static bool IsEnding(this MarkdownTokens flags)
|
||||||
|
{
|
||||||
|
return flags.HasFlag(MarkdownTokens.LineEnding) || flags.HasFlag(MarkdownTokens.LineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsLineBreak(this MarkdownTokens flags)
|
||||||
|
{
|
||||||
|
return flags.HasFlag(MarkdownTokens.LineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ShouldPreserve(this MarkdownTokens flags)
|
||||||
|
{
|
||||||
|
return flags.HasFlag(MarkdownTokens.Preserve);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Help;
|
||||||
|
|
||||||
|
internal enum MarkdownTokenType
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
Text,
|
||||||
|
|
||||||
|
Header,
|
||||||
|
|
||||||
|
FencedBlock,
|
||||||
|
|
||||||
|
LineBreak,
|
||||||
|
|
||||||
|
ParagraphStart,
|
||||||
|
|
||||||
|
ParagraphEnd,
|
||||||
|
|
||||||
|
LinkReference,
|
||||||
|
|
||||||
|
Link,
|
||||||
|
|
||||||
|
LinkReferenceDefinition,
|
||||||
|
|
||||||
|
YamlKeyValue
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Help;
|
||||||
|
|
||||||
|
[Flags()]
|
||||||
|
internal enum MarkdownTokens
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
Italic = 1,
|
||||||
|
|
||||||
|
Bold = 2,
|
||||||
|
|
||||||
|
Code = 4,
|
||||||
|
|
||||||
|
LineEnding = 8,
|
||||||
|
|
||||||
|
LineBreak = 16,
|
||||||
|
|
||||||
|
Preserve = 32,
|
||||||
|
|
||||||
|
// Accelerators
|
||||||
|
PreserveLineEnding = 40
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace PSRule.Help;
|
||||||
|
|
||||||
|
[DebuggerDisplay("StartPos = (L: {Start}, C: {Column}), EndPos = (L: {End}, C: {Column.End}), Text = {Text}")]
|
||||||
|
internal sealed class SourceExtent
|
||||||
|
{
|
||||||
|
private readonly string _Source;
|
||||||
|
|
||||||
|
// Lazily cache extracted text
|
||||||
|
private string _Text;
|
||||||
|
|
||||||
|
internal SourceExtent(string source, string path, int start, int end, int line, int column)
|
||||||
|
{
|
||||||
|
_Text = null;
|
||||||
|
_Source = source;
|
||||||
|
Path = path;
|
||||||
|
Start = start;
|
||||||
|
End = end;
|
||||||
|
Line = line;
|
||||||
|
Column = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly string Path;
|
||||||
|
|
||||||
|
public readonly int Start;
|
||||||
|
|
||||||
|
public readonly int End;
|
||||||
|
|
||||||
|
public readonly int Line;
|
||||||
|
|
||||||
|
public readonly int Column;
|
||||||
|
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_Text == null)
|
||||||
|
{
|
||||||
|
_Text = _Source.Substring(Start, (End - Start));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,193 +6,6 @@ using System.Diagnostics;
|
||||||
|
|
||||||
namespace PSRule.Help;
|
namespace PSRule.Help;
|
||||||
|
|
||||||
internal static class TokenStreamExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Add a header.
|
|
||||||
/// </summary>
|
|
||||||
public static void Header(this TokenStream stream, int depth, string text, SourceExtent extent, bool lineBreak)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken()
|
|
||||||
{
|
|
||||||
Depth = depth,
|
|
||||||
Extent = extent,
|
|
||||||
Text = text,
|
|
||||||
Type = MarkdownTokenType.Header,
|
|
||||||
Flag = lineBreak ? MarkdownTokens.LineBreak : MarkdownTokens.LineEnding | MarkdownTokens.Preserve
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void YamlKeyValue(this TokenStream stream, string key, string value)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken()
|
|
||||||
{
|
|
||||||
Meta = key,
|
|
||||||
Text = value,
|
|
||||||
Type = MarkdownTokenType.YamlKeyValue
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a code fence.
|
|
||||||
/// </summary>
|
|
||||||
public static void FencedBlock(this TokenStream stream, string meta, string text, SourceExtent extent, bool lineBreak)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken()
|
|
||||||
{
|
|
||||||
Extent = extent,
|
|
||||||
Meta = meta,
|
|
||||||
Text = text,
|
|
||||||
Type = MarkdownTokenType.FencedBlock,
|
|
||||||
Flag = (lineBreak ? MarkdownTokens.LineBreak : MarkdownTokens.LineEnding) | MarkdownTokens.Preserve
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a line break.
|
|
||||||
/// </summary>
|
|
||||||
public static void LineBreak(this TokenStream stream, int count)
|
|
||||||
{
|
|
||||||
// Ignore line break at the very start of file
|
|
||||||
if (stream.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.LineBreak, Flag = MarkdownTokens.LineBreak });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Text(this TokenStream stream, string text, MarkdownTokens flag = MarkdownTokens.None)
|
|
||||||
{
|
|
||||||
if (MergeText(stream.Current, text, flag))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.Text, Text = text, Flag = flag });
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool MergeText(MarkdownToken current, string text, MarkdownTokens flag)
|
|
||||||
{
|
|
||||||
// Only allow merge if the previous token was text
|
|
||||||
if (current == null || current.Type != MarkdownTokenType.Text)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current.Flag.ShouldPreserve())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the previous token was text, lessen the break but still don't allow merging
|
|
||||||
if (current.Flag.HasFlag(MarkdownTokens.LineBreak) && !current.Flag.ShouldPreserve())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text must have the same flags set
|
|
||||||
if (current.Flag.HasFlag(MarkdownTokens.Italic) != flag.HasFlag(MarkdownTokens.Italic))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current.Flag.HasFlag(MarkdownTokens.Bold) != flag.HasFlag(MarkdownTokens.Bold))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current.Flag.HasFlag(MarkdownTokens.Code) != flag.HasFlag(MarkdownTokens.Code))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!current.Flag.IsEnding())
|
|
||||||
{
|
|
||||||
current.Text = string.Concat(current.Text, text);
|
|
||||||
}
|
|
||||||
else if (current.Flag == MarkdownTokens.LineEnding)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take on the ending of the merged token
|
|
||||||
current.Flag = flag;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Link(this TokenStream stream, string text, string uri)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.Link, Meta = text, Text = uri });
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void LinkReference(this TokenStream stream, string text, string linkRef)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.LinkReference, Meta = text, Text = linkRef });
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void LinkReferenceDefinition(this TokenStream stream, string text, string linkTarget)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.LinkReferenceDefinition, Meta = text, Text = linkTarget });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a marker for the start of a paragraph.
|
|
||||||
/// </summary>
|
|
||||||
public static void ParagraphStart(this TokenStream stream)
|
|
||||||
{
|
|
||||||
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.ParagraphStart });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a marker for the end of a paragraph.
|
|
||||||
/// </summary>
|
|
||||||
public static void ParagraphEnd(this TokenStream stream)
|
|
||||||
{
|
|
||||||
if (stream.Count > 0)
|
|
||||||
{
|
|
||||||
if (stream.Current.Type == MarkdownTokenType.ParagraphStart)
|
|
||||||
{
|
|
||||||
stream.Pop();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.ParagraphEnd });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<MarkdownToken> GetSection(this TokenStream stream, string header)
|
|
||||||
{
|
|
||||||
return stream.Count == 0
|
|
||||||
? Enumerable.Empty<MarkdownToken>()
|
|
||||||
: stream
|
|
||||||
// Skip until we reach the header
|
|
||||||
.SkipWhile(token => token.Type != MarkdownTokenType.Header || token.Text != header)
|
|
||||||
|
|
||||||
// Get all tokens to the next header
|
|
||||||
.Skip(1)
|
|
||||||
.TakeWhile(token => token.Type != MarkdownTokenType.Header);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<MarkdownToken> GetSections(this TokenStream stream)
|
|
||||||
{
|
|
||||||
return stream.Count == 0
|
|
||||||
? Enumerable.Empty<MarkdownToken>()
|
|
||||||
: stream
|
|
||||||
// Skip until we reach the header
|
|
||||||
.SkipWhile(token => token.Type != MarkdownTokenType.Header)
|
|
||||||
|
|
||||||
// Get all tokens to the next header
|
|
||||||
.Skip(1)
|
|
||||||
.TakeWhile(token => token.Type != MarkdownTokenType.Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerDisplay("Current = {Current?.Text}")]
|
[DebuggerDisplay("Current = {Current?.Text}")]
|
||||||
internal sealed class TokenStream : IEnumerable<MarkdownToken>
|
internal sealed class TokenStream : IEnumerable<MarkdownToken>
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Help;
|
||||||
|
|
||||||
|
internal static class TokenStreamExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add a header.
|
||||||
|
/// </summary>
|
||||||
|
public static void Header(this TokenStream stream, int depth, string text, SourceExtent extent, bool lineBreak)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken()
|
||||||
|
{
|
||||||
|
Depth = depth,
|
||||||
|
Extent = extent,
|
||||||
|
Text = text,
|
||||||
|
Type = MarkdownTokenType.Header,
|
||||||
|
Flag = lineBreak ? MarkdownTokens.LineBreak : MarkdownTokens.LineEnding | MarkdownTokens.Preserve
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void YamlKeyValue(this TokenStream stream, string key, string value)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken()
|
||||||
|
{
|
||||||
|
Meta = key,
|
||||||
|
Text = value,
|
||||||
|
Type = MarkdownTokenType.YamlKeyValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a code fence.
|
||||||
|
/// </summary>
|
||||||
|
public static void FencedBlock(this TokenStream stream, string meta, string text, SourceExtent extent, bool lineBreak)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken()
|
||||||
|
{
|
||||||
|
Extent = extent,
|
||||||
|
Meta = meta,
|
||||||
|
Text = text,
|
||||||
|
Type = MarkdownTokenType.FencedBlock,
|
||||||
|
Flag = (lineBreak ? MarkdownTokens.LineBreak : MarkdownTokens.LineEnding) | MarkdownTokens.Preserve
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a line break.
|
||||||
|
/// </summary>
|
||||||
|
public static void LineBreak(this TokenStream stream, int count)
|
||||||
|
{
|
||||||
|
// Ignore line break at the very start of file
|
||||||
|
if (stream.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.LineBreak, Flag = MarkdownTokens.LineBreak });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Text(this TokenStream stream, string text, MarkdownTokens flag = MarkdownTokens.None)
|
||||||
|
{
|
||||||
|
if (MergeText(stream.Current, text, flag))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.Text, Text = text, Flag = flag });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool MergeText(MarkdownToken current, string text, MarkdownTokens flag)
|
||||||
|
{
|
||||||
|
// Only allow merge if the previous token was text
|
||||||
|
if (current == null || current.Type != MarkdownTokenType.Text)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.Flag.ShouldPreserve())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the previous token was text, lessen the break but still don't allow merging
|
||||||
|
if (current.Flag.HasFlag(MarkdownTokens.LineBreak) && !current.Flag.ShouldPreserve())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text must have the same flags set
|
||||||
|
if (current.Flag.HasFlag(MarkdownTokens.Italic) != flag.HasFlag(MarkdownTokens.Italic))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.Flag.HasFlag(MarkdownTokens.Bold) != flag.HasFlag(MarkdownTokens.Bold))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.Flag.HasFlag(MarkdownTokens.Code) != flag.HasFlag(MarkdownTokens.Code))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current.Flag.IsEnding())
|
||||||
|
{
|
||||||
|
current.Text = string.Concat(current.Text, text);
|
||||||
|
}
|
||||||
|
else if (current.Flag == MarkdownTokens.LineEnding)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take on the ending of the merged token
|
||||||
|
current.Flag = flag;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Link(this TokenStream stream, string text, string uri)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.Link, Meta = text, Text = uri });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LinkReference(this TokenStream stream, string text, string linkRef)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.LinkReference, Meta = text, Text = linkRef });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LinkReferenceDefinition(this TokenStream stream, string text, string linkTarget)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.LinkReferenceDefinition, Meta = text, Text = linkTarget });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a marker for the start of a paragraph.
|
||||||
|
/// </summary>
|
||||||
|
public static void ParagraphStart(this TokenStream stream)
|
||||||
|
{
|
||||||
|
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.ParagraphStart });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a marker for the end of a paragraph.
|
||||||
|
/// </summary>
|
||||||
|
public static void ParagraphEnd(this TokenStream stream)
|
||||||
|
{
|
||||||
|
if (stream.Count > 0)
|
||||||
|
{
|
||||||
|
if (stream.Current.Type == MarkdownTokenType.ParagraphStart)
|
||||||
|
{
|
||||||
|
stream.Pop();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Add(new MarkdownToken() { Type = MarkdownTokenType.ParagraphEnd });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<MarkdownToken> GetSection(this TokenStream stream, string header)
|
||||||
|
{
|
||||||
|
return stream.Count == 0
|
||||||
|
? Enumerable.Empty<MarkdownToken>()
|
||||||
|
: stream
|
||||||
|
// Skip until we reach the header
|
||||||
|
.SkipWhile(token => token.Type != MarkdownTokenType.Header || token.Text != header)
|
||||||
|
|
||||||
|
// Get all tokens to the next header
|
||||||
|
.Skip(1)
|
||||||
|
.TakeWhile(token => token.Type != MarkdownTokenType.Header);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<MarkdownToken> GetSections(this TokenStream stream)
|
||||||
|
{
|
||||||
|
return stream.Count == 0
|
||||||
|
? Enumerable.Empty<MarkdownToken>()
|
||||||
|
: stream
|
||||||
|
// Skip until we reach the header
|
||||||
|
.SkipWhile(token => token.Type != MarkdownTokenType.Header)
|
||||||
|
|
||||||
|
// Get all tokens to the next header
|
||||||
|
.Skip(1)
|
||||||
|
.TakeWhile(token => token.Type != MarkdownTokenType.Header);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSRule.Runtime;
|
||||||
|
|
||||||
|
namespace PSRule.Host;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An assertion helper variable $Assert used during Rule execution.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class AssertVariable : PSVariable
|
||||||
|
{
|
||||||
|
private const string VARIABLE_NAME = "Assert";
|
||||||
|
private readonly Assert _Value;
|
||||||
|
|
||||||
|
public AssertVariable()
|
||||||
|
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
||||||
|
{
|
||||||
|
_Value = new Assert();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Value => _Value;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSRule.Runtime;
|
||||||
|
|
||||||
|
namespace PSRule.Host;
|
||||||
|
|
||||||
|
internal sealed class ConfigurationVariable : PSVariable
|
||||||
|
{
|
||||||
|
private const string VARIABLE_NAME = "Configuration";
|
||||||
|
private readonly Runtime.Configuration _Value;
|
||||||
|
|
||||||
|
public ConfigurationVariable()
|
||||||
|
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
||||||
|
{
|
||||||
|
_Value = new Runtime.Configuration(RunspaceContext.CurrentThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Value => _Value;
|
||||||
|
}
|
|
@ -4,111 +4,9 @@
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
using System.Management.Automation.Runspaces;
|
using System.Management.Automation.Runspaces;
|
||||||
using PSRule.Commands;
|
using PSRule.Commands;
|
||||||
using PSRule.Runtime;
|
|
||||||
|
|
||||||
namespace PSRule.Host;
|
namespace PSRule.Host;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A dynamic variable $PSRule used during Rule execution.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class PSRuleVariable : PSVariable
|
|
||||||
{
|
|
||||||
private const string VARIABLE_NAME = "PSRule";
|
|
||||||
|
|
||||||
private readonly Runtime.PSRule _Value;
|
|
||||||
|
|
||||||
public PSRuleVariable()
|
|
||||||
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
|
||||||
{
|
|
||||||
_Value = new Runtime.PSRule(RunspaceContext.CurrentThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Value => _Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A dynamic variable $Rule used during Rule execution.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class RuleVariable : PSVariable
|
|
||||||
{
|
|
||||||
private const string VARIABLE_NAME = "Rule";
|
|
||||||
|
|
||||||
private readonly Rule _Value;
|
|
||||||
|
|
||||||
public RuleVariable()
|
|
||||||
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
|
||||||
{
|
|
||||||
_Value = new Rule();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Value => _Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A dynamic variable $LocalizedData used during Rule execution.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class LocalizedDataVariable : PSVariable
|
|
||||||
{
|
|
||||||
private const string VARIABLE_NAME = "LocalizedData";
|
|
||||||
|
|
||||||
private readonly LocalizedData _Value;
|
|
||||||
|
|
||||||
public LocalizedDataVariable()
|
|
||||||
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
|
||||||
{
|
|
||||||
_Value = new LocalizedData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Value => _Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An assertion helper variable $Assert used during Rule execution.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class AssertVariable : PSVariable
|
|
||||||
{
|
|
||||||
private const string VARIABLE_NAME = "Assert";
|
|
||||||
private readonly Assert _Value;
|
|
||||||
|
|
||||||
public AssertVariable()
|
|
||||||
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
|
||||||
{
|
|
||||||
_Value = new Assert();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Value => _Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A dynamic variable used during Rule execution.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class TargetObjectVariable : PSVariable
|
|
||||||
{
|
|
||||||
private const string VARIABLE_NAME = "TargetObject";
|
|
||||||
|
|
||||||
public TargetObjectVariable()
|
|
||||||
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Value => RunspaceContext.CurrentThread.TargetObject?.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class ConfigurationVariable : PSVariable
|
|
||||||
{
|
|
||||||
private const string VARIABLE_NAME = "Configuration";
|
|
||||||
private readonly Runtime.Configuration _Value;
|
|
||||||
|
|
||||||
public ConfigurationVariable()
|
|
||||||
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
|
||||||
{
|
|
||||||
_Value = new Runtime.Configuration(RunspaceContext.CurrentThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Value => _Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class HostState
|
internal static class HostState
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSRule.Runtime;
|
||||||
|
|
||||||
|
namespace PSRule.Host;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dynamic variable $LocalizedData used during Rule execution.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class LocalizedDataVariable : PSVariable
|
||||||
|
{
|
||||||
|
private const string VARIABLE_NAME = "LocalizedData";
|
||||||
|
|
||||||
|
private readonly LocalizedData _Value;
|
||||||
|
|
||||||
|
public LocalizedDataVariable()
|
||||||
|
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
||||||
|
{
|
||||||
|
_Value = new LocalizedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Value => _Value;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSRule.Runtime;
|
||||||
|
|
||||||
|
namespace PSRule.Host;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dynamic variable $PSRule used during Rule execution.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class PSRuleVariable : PSVariable
|
||||||
|
{
|
||||||
|
private const string VARIABLE_NAME = "PSRule";
|
||||||
|
|
||||||
|
private readonly Runtime.PSRule _Value;
|
||||||
|
|
||||||
|
public PSRuleVariable()
|
||||||
|
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
||||||
|
{
|
||||||
|
_Value = new Runtime.PSRule(RunspaceContext.CurrentThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Value => _Value;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSRule.Runtime;
|
||||||
|
|
||||||
|
namespace PSRule.Host;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dynamic variable $Rule used during Rule execution.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class RuleVariable : PSVariable
|
||||||
|
{
|
||||||
|
private const string VARIABLE_NAME = "Rule";
|
||||||
|
|
||||||
|
private readonly Rule _Value;
|
||||||
|
|
||||||
|
public RuleVariable()
|
||||||
|
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
||||||
|
{
|
||||||
|
_Value = new Rule();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Value => _Value;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSRule.Runtime;
|
||||||
|
|
||||||
|
namespace PSRule.Host;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dynamic variable used during Rule execution.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class TargetObjectVariable : PSVariable
|
||||||
|
{
|
||||||
|
private const string VARIABLE_NAME = "TargetObject";
|
||||||
|
|
||||||
|
public TargetObjectVariable()
|
||||||
|
: base(VARIABLE_NAME, null, ScopedItemOptions.ReadOnly)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Value => RunspaceContext.CurrentThread.TargetObject?.Value;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using PSRule.Data;
|
||||||
|
|
||||||
|
namespace PSRule.Pipeline.Dependencies;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An entry within the lock file.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LockEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The version to use.
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("version", NullValueHandling = NullValueHandling.Include)]
|
||||||
|
public SemanticVersion.Version Version { get; set; }
|
||||||
|
}
|
|
@ -3,22 +3,9 @@
|
||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using PSRule.Data;
|
|
||||||
|
|
||||||
namespace PSRule.Pipeline.Dependencies;
|
namespace PSRule.Pipeline.Dependencies;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An entry within the lock file.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class LockEntry
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The version to use.
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("version", NullValueHandling = NullValueHandling.Include)]
|
|
||||||
public SemanticVersion.Version Version { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define the structure for the PSRule lock file.
|
/// Define the structure for the PSRule lock file.
|
||||||
/// By default, this file is <c>ps-rule.lock.json</c>.
|
/// By default, this file is <c>ps-rule.lock.json</c>.
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
|
|
||||||
using PSRule.Emitters;
|
using PSRule.Emitters;
|
||||||
|
|
||||||
namespace PSRule.Pipeline.Emitters
|
namespace PSRule.Pipeline.Emitters;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A chain of emitters.
|
/// A chain of emitters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal delegate bool EmitterChain(IEmitterContext context, object o, Type type);
|
internal delegate bool EmitterChain(IEmitterContext context, object o, Type type);
|
||||||
}
|
|
||||||
|
|
|
@ -4,51 +4,50 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using PSRule.Data;
|
using PSRule.Data;
|
||||||
|
|
||||||
namespace PSRule.Pipeline.Emitters
|
namespace PSRule.Pipeline.Emitters;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{Path}")]
|
||||||
|
internal sealed class InternalFileInfo : IFileInfo, IDisposable
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{Path}")]
|
private FileStream _Stream;
|
||||||
internal sealed class InternalFileInfo : IFileInfo, IDisposable
|
private bool _Disposed;
|
||||||
|
|
||||||
|
public InternalFileInfo(string path, string extension)
|
||||||
{
|
{
|
||||||
private FileStream _Stream;
|
Path = path;
|
||||||
private bool _Disposed;
|
Extension = extension;
|
||||||
|
}
|
||||||
|
|
||||||
public InternalFileInfo(string path, string extension)
|
/// <inheritdoc/>
|
||||||
|
public string Path { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string Extension { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IFileStream GetFileStream()
|
||||||
|
{
|
||||||
|
_Stream ??= File.Open(Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
_Stream.Position = 0;
|
||||||
|
return new InternalFileStream(this, _Stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_Disposed)
|
||||||
{
|
{
|
||||||
Path = path;
|
if (disposing)
|
||||||
Extension = extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string Path { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string Extension { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IFileStream GetFileStream()
|
|
||||||
{
|
|
||||||
_Stream ??= File.Open(Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
||||||
_Stream.Position = 0;
|
|
||||||
return new InternalFileStream(this, _Stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!_Disposed)
|
|
||||||
{
|
{
|
||||||
if (disposing)
|
_Stream.Dispose();
|
||||||
{
|
|
||||||
_Stream.Dispose();
|
|
||||||
}
|
|
||||||
_Disposed = true;
|
|
||||||
}
|
}
|
||||||
}
|
_Disposed = true;
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
||||||
Dispose(disposing: true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,52 +4,51 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using PSRule.Data;
|
using PSRule.Data;
|
||||||
|
|
||||||
namespace PSRule.Pipeline.Emitters
|
namespace PSRule.Pipeline.Emitters;
|
||||||
|
|
||||||
|
internal sealed class InternalFileStream : IFileStream
|
||||||
{
|
{
|
||||||
internal sealed class InternalFileStream : IFileStream
|
private readonly Stream _Stream;
|
||||||
|
|
||||||
|
private bool _Disposed;
|
||||||
|
|
||||||
|
internal InternalFileStream(IFileInfo info, Stream stream)
|
||||||
{
|
{
|
||||||
private readonly Stream _Stream;
|
Info = info;
|
||||||
|
_Stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
private bool _Disposed;
|
/// <inheritdoc/>
|
||||||
|
public IFileInfo Info { get; }
|
||||||
|
|
||||||
internal InternalFileStream(IFileInfo info, Stream stream)
|
/// <inheritdoc/>
|
||||||
|
public TextReader AsTextReader()
|
||||||
|
{
|
||||||
|
return new StreamReader(
|
||||||
|
stream: _Stream,
|
||||||
|
encoding: Encoding.UTF8,
|
||||||
|
detectEncodingFromByteOrderMarks: true,
|
||||||
|
bufferSize: 1024,
|
||||||
|
leaveOpen: true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_Disposed)
|
||||||
{
|
{
|
||||||
Info = info;
|
if (disposing)
|
||||||
_Stream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IFileInfo Info { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TextReader AsTextReader()
|
|
||||||
{
|
|
||||||
return new StreamReader(
|
|
||||||
stream: _Stream,
|
|
||||||
encoding: Encoding.UTF8,
|
|
||||||
detectEncodingFromByteOrderMarks: true,
|
|
||||||
bufferSize: 1024,
|
|
||||||
leaveOpen: true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!_Disposed)
|
|
||||||
{
|
{
|
||||||
if (disposing)
|
_Stream?.Dispose();
|
||||||
{
|
|
||||||
_Stream?.Dispose();
|
|
||||||
}
|
|
||||||
_Disposed = true;
|
|
||||||
}
|
}
|
||||||
}
|
_Disposed = true;
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
||||||
Dispose(disposing: true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,9 @@ internal sealed class JsonEmitter : FileEmitter
|
||||||
|
|
||||||
public JsonEmitter()
|
public JsonEmitter()
|
||||||
{
|
{
|
||||||
|
|
||||||
_Settings = new JsonSerializerSettings
|
_Settings = new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
|
|
||||||
};
|
};
|
||||||
_Deserializer = JsonSerializer.CreateDefault(_Settings); // Think about caching this.
|
_Deserializer = JsonSerializer.CreateDefault(_Settings); // Think about caching this.
|
||||||
_Deserializer.Converters.Add(new PSObjectArrayJsonConverter(null));
|
_Deserializer.Converters.Add(new PSObjectArrayJsonConverter(null));
|
||||||
|
@ -88,13 +87,13 @@ internal sealed class JsonEmitter : FileEmitter
|
||||||
VisitItems(context, value, null);
|
VisitItems(context, value, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ internal sealed class YamlEmitter : FileEmitter
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,115 +9,6 @@ using PSRule.Rules;
|
||||||
|
|
||||||
namespace PSRule.Pipeline.Formatters;
|
namespace PSRule.Pipeline.Formatters;
|
||||||
|
|
||||||
internal interface IAssertFormatter : IPipelineWriter
|
|
||||||
{
|
|
||||||
void Result(InvokeResult result);
|
|
||||||
|
|
||||||
void Error(ErrorRecord errorRecord);
|
|
||||||
|
|
||||||
void Warning(WarningRecord warningRecord);
|
|
||||||
|
|
||||||
void End(int total, int fail, int error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configures formatted output.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class TerminalSupport
|
|
||||||
{
|
|
||||||
public TerminalSupport(int indent)
|
|
||||||
{
|
|
||||||
BodyIndent = new string(' ', indent);
|
|
||||||
MessageIdent = BodyIndent;
|
|
||||||
StartResultIndent = FormatterStrings.StartObjectPrefix;
|
|
||||||
SourceLocationPrefix = "| ";
|
|
||||||
SynopsisPrefix = FormatterStrings.SynopsisPrefix;
|
|
||||||
PassStatus = FormatterStrings.Result_Pass;
|
|
||||||
FailStatus = FormatterStrings.Result_Fail;
|
|
||||||
InformationStatus = FormatterStrings.Result_Information;
|
|
||||||
WarningStatus = FormatterStrings.Result_Warning;
|
|
||||||
ErrorStatus = FormatterStrings.Result_Error;
|
|
||||||
RecommendationHeading = FormatterStrings.Recommend;
|
|
||||||
RecommendationPrefix = "| ";
|
|
||||||
ReasonHeading = FormatterStrings.Reason;
|
|
||||||
ReasonItemPrefix = "| - ";
|
|
||||||
HelpHeading = FormatterStrings.Help;
|
|
||||||
HelpLinkPrefix = "| - ";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BodyIndent { get; }
|
|
||||||
|
|
||||||
public string MessageIdent { get; internal set; }
|
|
||||||
|
|
||||||
public string StartResultIndent { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? StartResultForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public string SourceLocationPrefix { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? SourceLocationForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public string SynopsisPrefix { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? SynopsisForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public string PassStatus { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? PassForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? PassBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? PassStatusBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? PassStatusForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public string FailStatus { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? FailForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? FailBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? FailStatusBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? FailStatusForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public string InformationStatus { get; internal set; }
|
|
||||||
|
|
||||||
public string WarningStatus { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? WarningForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? WarningBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? WarningStatusBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? WarningStatusForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public string ErrorStatus { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? ErrorForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? ErrorBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? ErrorStatusBackgroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? ErrorStatusForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public ConsoleColor? BodyForegroundColor { get; internal set; }
|
|
||||||
|
|
||||||
public string RecommendationHeading { get; internal set; }
|
|
||||||
|
|
||||||
public string RecommendationPrefix { get; internal set; }
|
|
||||||
|
|
||||||
public string ReasonHeading { get; internal set; }
|
|
||||||
|
|
||||||
public string ReasonItemPrefix { get; internal set; }
|
|
||||||
|
|
||||||
public string HelpHeading { get; internal set; }
|
|
||||||
|
|
||||||
public string HelpLinkPrefix { get; internal set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A base class for a formatter.
|
/// A base class for a formatter.
|
||||||
/// </summary>
|
/// </summary>
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Management.Automation;
|
||||||
|
|
||||||
|
namespace PSRule.Pipeline.Formatters;
|
||||||
|
|
||||||
|
internal interface IAssertFormatter : IPipelineWriter
|
||||||
|
{
|
||||||
|
void Result(InvokeResult result);
|
||||||
|
|
||||||
|
void Error(ErrorRecord errorRecord);
|
||||||
|
|
||||||
|
void Warning(WarningRecord warningRecord);
|
||||||
|
|
||||||
|
void End(int total, int fail, int error);
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using PSRule.Resources;
|
||||||
|
|
||||||
|
namespace PSRule.Pipeline.Formatters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures formatted output.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class TerminalSupport
|
||||||
|
{
|
||||||
|
public TerminalSupport(int indent)
|
||||||
|
{
|
||||||
|
BodyIndent = new string(' ', indent);
|
||||||
|
MessageIdent = BodyIndent;
|
||||||
|
StartResultIndent = FormatterStrings.StartObjectPrefix;
|
||||||
|
SourceLocationPrefix = "| ";
|
||||||
|
SynopsisPrefix = FormatterStrings.SynopsisPrefix;
|
||||||
|
PassStatus = FormatterStrings.Result_Pass;
|
||||||
|
FailStatus = FormatterStrings.Result_Fail;
|
||||||
|
InformationStatus = FormatterStrings.Result_Information;
|
||||||
|
WarningStatus = FormatterStrings.Result_Warning;
|
||||||
|
ErrorStatus = FormatterStrings.Result_Error;
|
||||||
|
RecommendationHeading = FormatterStrings.Recommend;
|
||||||
|
RecommendationPrefix = "| ";
|
||||||
|
ReasonHeading = FormatterStrings.Reason;
|
||||||
|
ReasonItemPrefix = "| - ";
|
||||||
|
HelpHeading = FormatterStrings.Help;
|
||||||
|
HelpLinkPrefix = "| - ";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BodyIndent { get; }
|
||||||
|
|
||||||
|
public string MessageIdent { get; internal set; }
|
||||||
|
|
||||||
|
public string StartResultIndent { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? StartResultForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public string SourceLocationPrefix { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? SourceLocationForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public string SynopsisPrefix { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? SynopsisForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public string PassStatus { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? PassForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? PassBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? PassStatusBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? PassStatusForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public string FailStatus { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? FailForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? FailBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? FailStatusBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? FailStatusForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public string InformationStatus { get; internal set; }
|
||||||
|
|
||||||
|
public string WarningStatus { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? WarningForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? WarningBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? WarningStatusBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? WarningStatusForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public string ErrorStatus { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? ErrorForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? ErrorBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? ErrorStatusBackgroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? ErrorStatusForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public ConsoleColor? BodyForegroundColor { get; internal set; }
|
||||||
|
|
||||||
|
public string RecommendationHeading { get; internal set; }
|
||||||
|
|
||||||
|
public string RecommendationPrefix { get; internal set; }
|
||||||
|
|
||||||
|
public string ReasonHeading { get; internal set; }
|
||||||
|
|
||||||
|
public string ReasonItemPrefix { get; internal set; }
|
||||||
|
|
||||||
|
public string HelpHeading { get; internal set; }
|
||||||
|
|
||||||
|
public string HelpLinkPrefix { get; internal set; }
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using PSRule.Definitions;
|
||||||
|
|
||||||
|
namespace PSRule.Rules;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A rule help information structure.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRuleHelpInfoV2 : IResourceHelpInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The rule recommendation.
|
||||||
|
/// </summary>
|
||||||
|
InfoString Recommendation { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional annotations, which are string key/ value pairs.
|
||||||
|
/// </summary>
|
||||||
|
Hashtable Annotations { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the module where the rule was loaded from.
|
||||||
|
/// </summary>
|
||||||
|
string ModuleName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional online links to reference information for the rule.
|
||||||
|
/// </summary>
|
||||||
|
Link[] Links { get; }
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Rules;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An URL link to reference information.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Link
|
||||||
|
{
|
||||||
|
internal Link(string name, string uri)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The display name of the link.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The URL to the information, or the target link.
|
||||||
|
/// </summary>
|
||||||
|
public string Uri { get; }
|
||||||
|
}
|
|
@ -9,108 +9,6 @@ using YamlDotNet.Serialization;
|
||||||
|
|
||||||
namespace PSRule.Rules;
|
namespace PSRule.Rules;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A rule help information structure.
|
|
||||||
/// </summary>
|
|
||||||
public interface IRuleHelpInfoV2 : IResourceHelpInfo
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The rule recommendation.
|
|
||||||
/// </summary>
|
|
||||||
InfoString Recommendation { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional annotations, which are string key/ value pairs.
|
|
||||||
/// </summary>
|
|
||||||
Hashtable Annotations { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the module where the rule was loaded from.
|
|
||||||
/// </summary>
|
|
||||||
string ModuleName { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional online links to reference information for the rule.
|
|
||||||
/// </summary>
|
|
||||||
Link[] Links { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for rule help information.
|
|
||||||
/// </summary>
|
|
||||||
public static class RuleHelpInfoExtensions
|
|
||||||
{
|
|
||||||
private const string ONLINE_HELP_LINK_ANNOTATION = "online version";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the URI for the online version of the documentation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Returns the URI when a valid link is set, otherwise null is returned.</returns>
|
|
||||||
public static Uri GetOnlineHelpUri(this IRuleHelpInfoV2 info)
|
|
||||||
{
|
|
||||||
var link = GetOnlineHelpUrl(info);
|
|
||||||
return link == null ||
|
|
||||||
!Uri.TryCreate(link, UriKind.Absolute, out var result) ?
|
|
||||||
null : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the URL for the online version of the documentation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Returns the URL when set, otherwise null is returned.</returns>
|
|
||||||
public static string GetOnlineHelpUrl(this IRuleHelpInfoV2 info)
|
|
||||||
{
|
|
||||||
return info == null ||
|
|
||||||
info.Annotations == null ||
|
|
||||||
!info.Annotations.ContainsKey(ONLINE_HELP_LINK_ANNOTATION) ?
|
|
||||||
null : info.Annotations[ONLINE_HELP_LINK_ANNOTATION].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines if the online help link is set.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Returns <c>true</c> when the online help link is set. Otherwise this method returns <c>false</c>.</returns>
|
|
||||||
internal static bool HasOnlineHelp(this IRuleHelpInfoV2 info)
|
|
||||||
{
|
|
||||||
return info != null &&
|
|
||||||
info.Annotations != null &&
|
|
||||||
info.Annotations.ContainsKey(ONLINE_HELP_LINK_ANNOTATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the online help link from the <paramref name="url"/> parameter.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The info object.</param>
|
|
||||||
/// <param name="url">A URL to the online help location.</param>
|
|
||||||
internal static void SetOnlineHelpUrl(this IRuleHelpInfoV2 info, string url)
|
|
||||||
{
|
|
||||||
if (info == null || info.Annotations == null || string.IsNullOrEmpty(url)) return;
|
|
||||||
info.Annotations[ONLINE_HELP_LINK_ANNOTATION] = url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An URL link to reference information.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class Link
|
|
||||||
{
|
|
||||||
internal Link(string name, string uri)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Uri = uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The display name of the link.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The URL to the information, or the target link.
|
|
||||||
/// </summary>
|
|
||||||
public string Uri { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Output view helper class for rule help.
|
/// Output view helper class for rule help.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Rules;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for rule help information.
|
||||||
|
/// </summary>
|
||||||
|
public static class RuleHelpInfoExtensions
|
||||||
|
{
|
||||||
|
private const string ONLINE_HELP_LINK_ANNOTATION = "online version";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the URI for the online version of the documentation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns the URI when a valid link is set, otherwise null is returned.</returns>
|
||||||
|
public static Uri GetOnlineHelpUri(this IRuleHelpInfoV2 info)
|
||||||
|
{
|
||||||
|
var link = GetOnlineHelpUrl(info);
|
||||||
|
return link == null ||
|
||||||
|
!Uri.TryCreate(link, UriKind.Absolute, out var result) ?
|
||||||
|
null : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the URL for the online version of the documentation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns the URL when set, otherwise null is returned.</returns>
|
||||||
|
public static string GetOnlineHelpUrl(this IRuleHelpInfoV2 info)
|
||||||
|
{
|
||||||
|
return info == null ||
|
||||||
|
info.Annotations == null ||
|
||||||
|
!info.Annotations.ContainsKey(ONLINE_HELP_LINK_ANNOTATION) ?
|
||||||
|
null : info.Annotations[ONLINE_HELP_LINK_ANNOTATION].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the online help link is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns <c>true</c> when the online help link is set. Otherwise this method returns <c>false</c>.</returns>
|
||||||
|
internal static bool HasOnlineHelp(this IRuleHelpInfoV2 info)
|
||||||
|
{
|
||||||
|
return info != null &&
|
||||||
|
info.Annotations != null &&
|
||||||
|
info.Annotations.ContainsKey(ONLINE_HELP_LINK_ANNOTATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the online help link from the <paramref name="url"/> parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">The info object.</param>
|
||||||
|
/// <param name="url">A URL to the online help location.</param>
|
||||||
|
internal static void SetOnlineHelpUrl(this IRuleHelpInfoV2 info, string url)
|
||||||
|
{
|
||||||
|
if (info == null || info.Annotations == null || string.IsNullOrEmpty(url)) return;
|
||||||
|
info.Annotations[ONLINE_HELP_LINK_ANNOTATION] = url;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using PSRule.Configuration;
|
||||||
|
using PSRule.Definitions;
|
||||||
|
using PSRule.Pipeline;
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A named scope for language elements.
|
||||||
|
/// </summary>
|
||||||
|
internal interface ILanguageScope : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the scope.
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
BindingOption Binding { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get an ordered culture preference list which will be tries for finding help.
|
||||||
|
/// </summary>
|
||||||
|
string[] Culture { get; }
|
||||||
|
|
||||||
|
void Configure(OptionContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to get a specific configuration value by name.
|
||||||
|
/// </summary>
|
||||||
|
bool TryConfigurationValue(string key, out object value);
|
||||||
|
|
||||||
|
void WithFilter(IResourceFilter resourceFilter);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a filter for a specific resource kind.
|
||||||
|
/// </summary>
|
||||||
|
IResourceFilter GetFilter(ResourceKind kind);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a service to the scope.
|
||||||
|
/// </summary>
|
||||||
|
void AddService(string name, object service);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a previously added service.
|
||||||
|
/// </summary>
|
||||||
|
object GetService(string name);
|
||||||
|
|
||||||
|
bool TryGetType(object o, out string type, out string path);
|
||||||
|
|
||||||
|
bool TryGetName(object o, out string name, out string path);
|
||||||
|
|
||||||
|
bool TryGetScope(object o, out string[] scope);
|
||||||
|
}
|
|
@ -3,25 +3,6 @@
|
||||||
|
|
||||||
namespace PSRule.Runtime;
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A set of log levels which indicate different types of diagnostic messages.
|
|
||||||
/// </summary>
|
|
||||||
[Flags]
|
|
||||||
internal enum LogLevel
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
Error = 1,
|
|
||||||
|
|
||||||
Warning = 2,
|
|
||||||
|
|
||||||
Info = 4,
|
|
||||||
|
|
||||||
Verbose = 8,
|
|
||||||
|
|
||||||
Debug = 16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A generic interface for diagnostic logging within PSRule.
|
/// A generic interface for diagnostic logging within PSRule.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -45,6 +26,6 @@ internal interface ILogger
|
||||||
/// Write an error from an exception.
|
/// Write an error from an exception.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="exception">The exception to write.</param>
|
/// <param name="exception">The exception to write.</param>
|
||||||
/// <param name="errorId">A string identififer for the error.</param>
|
/// <param name="errorId">A string identifier for the error.</param>
|
||||||
void Error(Exception exception, string errorId = null);
|
void Error(Exception exception, string errorId = null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An operand that is compared with PSRule expressions.
|
||||||
|
/// </summary>
|
||||||
|
public interface IOperand
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the operand.
|
||||||
|
/// </summary>
|
||||||
|
object Value { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of operand.
|
||||||
|
/// </summary>
|
||||||
|
OperandKind Kind { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The object path to the operand.
|
||||||
|
/// </summary>
|
||||||
|
string Path { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A logical prefix to add to the object path.
|
||||||
|
/// </summary>
|
||||||
|
string Prefix { get; set; }
|
||||||
|
}
|
|
@ -8,54 +8,6 @@ using PSRule.Pipeline;
|
||||||
|
|
||||||
namespace PSRule.Runtime;
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A named scope for language elements.
|
|
||||||
/// </summary>
|
|
||||||
internal interface ILanguageScope : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the scope.
|
|
||||||
/// </summary>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
BindingOption Binding { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get an ordered culture preference list which will be tries for finding help.
|
|
||||||
/// </summary>
|
|
||||||
string[] Culture { get; }
|
|
||||||
|
|
||||||
void Configure(OptionContext context);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try to get a specific configuration value by name.
|
|
||||||
/// </summary>
|
|
||||||
bool TryConfigurationValue(string key, out object value);
|
|
||||||
|
|
||||||
void WithFilter(IResourceFilter resourceFilter);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a filter for a specific resource kind.
|
|
||||||
/// </summary>
|
|
||||||
IResourceFilter GetFilter(ResourceKind kind);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a service to the scope.
|
|
||||||
/// </summary>
|
|
||||||
void AddService(string name, object service);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a previously added service.
|
|
||||||
/// </summary>
|
|
||||||
object GetService(string name);
|
|
||||||
|
|
||||||
bool TryGetType(object o, out string type, out string path);
|
|
||||||
|
|
||||||
bool TryGetName(object o, out string name, out string path);
|
|
||||||
|
|
||||||
bool TryGetScope(object o, out string[] scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerDisplay("{Name}")]
|
[DebuggerDisplay("{Name}")]
|
||||||
internal sealed class LanguageScope : ILanguageScope
|
internal sealed class LanguageScope : ILanguageScope
|
||||||
{
|
{
|
||||||
|
@ -225,102 +177,3 @@ internal sealed class LanguageScope : ILanguageScope
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A collection of <see cref="ILanguageScope"/>.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class LanguageScopeSet : IDisposable
|
|
||||||
{
|
|
||||||
private readonly RunspaceContext _Context;
|
|
||||||
private readonly Dictionary<string, ILanguageScope> _Scopes;
|
|
||||||
|
|
||||||
private ILanguageScope _Current;
|
|
||||||
private bool _Disposed;
|
|
||||||
|
|
||||||
public LanguageScopeSet(RunspaceContext context)
|
|
||||||
{
|
|
||||||
_Context = context;
|
|
||||||
_Scopes = new Dictionary<string, ILanguageScope>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
Import(null, out _Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILanguageScope Current
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _Current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IDisposable
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!_Disposed)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
// Release and dispose scopes
|
|
||||||
if (_Scopes != null && _Scopes.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var kv in _Scopes)
|
|
||||||
kv.Value.Dispose();
|
|
||||||
|
|
||||||
_Scopes.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_Disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
||||||
Dispose(disposing: true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion IDisposable
|
|
||||||
|
|
||||||
internal void Add(ILanguageScope languageScope)
|
|
||||||
{
|
|
||||||
_Scopes.Add(languageScope.Name, languageScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal 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)
|
|
||||||
{
|
|
||||||
return _Scopes.TryGetValue(GetScopeName(name), out scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool Import(string name, out ILanguageScope scope)
|
|
||||||
{
|
|
||||||
if (_Scopes.TryGetValue(GetScopeName(name), out scope))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
scope = new LanguageScope(_Context, name);
|
|
||||||
Add(scope);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetScopeName(string name)
|
|
||||||
{
|
|
||||||
return LanguageScope.Normalize(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of <see cref="ILanguageScope"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class LanguageScopeSet : IDisposable
|
||||||
|
{
|
||||||
|
private readonly RunspaceContext _Context;
|
||||||
|
private readonly Dictionary<string, ILanguageScope> _Scopes;
|
||||||
|
|
||||||
|
private ILanguageScope _Current;
|
||||||
|
private bool _Disposed;
|
||||||
|
|
||||||
|
public LanguageScopeSet(RunspaceContext context)
|
||||||
|
{
|
||||||
|
_Context = context;
|
||||||
|
_Scopes = new Dictionary<string, ILanguageScope>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
Import(null, out _Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILanguageScope Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_Disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Release and dispose scopes
|
||||||
|
if (_Scopes != null && _Scopes.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var kv in _Scopes)
|
||||||
|
kv.Value.Dispose();
|
||||||
|
|
||||||
|
_Scopes.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_Disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable
|
||||||
|
|
||||||
|
internal void Add(ILanguageScope languageScope)
|
||||||
|
{
|
||||||
|
_Scopes.Add(languageScope.Name, languageScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal 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)
|
||||||
|
{
|
||||||
|
return _Scopes.TryGetValue(GetScopeName(name), out scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool Import(string name, out ILanguageScope scope)
|
||||||
|
{
|
||||||
|
if (_Scopes.TryGetValue(GetScopeName(name), out scope))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
scope = new LanguageScope(_Context, name);
|
||||||
|
Add(scope);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetScopeName(string name)
|
||||||
|
{
|
||||||
|
return LanguageScope.Normalize(name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A set of log levels which indicate different types of diagnostic messages.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
internal enum LogLevel
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
Error = 1,
|
||||||
|
|
||||||
|
Warning = 2,
|
||||||
|
|
||||||
|
Info = 4,
|
||||||
|
|
||||||
|
Verbose = 8,
|
||||||
|
|
||||||
|
Debug = 16,
|
||||||
|
}
|
|
@ -5,27 +5,6 @@ using System.Diagnostics;
|
||||||
|
|
||||||
namespace PSRule.Runtime;
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of NameToken.
|
|
||||||
/// </summary>
|
|
||||||
internal enum NameTokenType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The token represents a field/ property of an object.
|
|
||||||
/// </summary>
|
|
||||||
Field = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The token is an index in an object.
|
|
||||||
/// </summary>
|
|
||||||
Index = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The token is a reference to the parent object. Can only be the first token.
|
|
||||||
/// </summary>
|
|
||||||
Self = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A token for expressing a path through a tree of fields.
|
/// A token for expressing a path through a tree of fields.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of NameToken.
|
||||||
|
/// </summary>
|
||||||
|
internal enum NameTokenType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The token represents a field/ property of an object.
|
||||||
|
/// </summary>
|
||||||
|
Field = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The token is an index in an object.
|
||||||
|
/// </summary>
|
||||||
|
Index = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The token is a reference to the parent object. Can only be the first token.
|
||||||
|
/// </summary>
|
||||||
|
Self = 2
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
internal enum FilterOperator
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
// Comparison
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
LessOrEqual,
|
||||||
|
Less,
|
||||||
|
GreaterOrEqual,
|
||||||
|
Greater,
|
||||||
|
RegEx,
|
||||||
|
|
||||||
|
// Logical
|
||||||
|
Or,
|
||||||
|
And,
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
internal interface IPathToken
|
||||||
|
{
|
||||||
|
PathTokenType Type { get; }
|
||||||
|
|
||||||
|
PathTokenOption Option { get; }
|
||||||
|
|
||||||
|
object Arg { get; }
|
||||||
|
|
||||||
|
T As<T>();
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
internal interface ITokenReader
|
||||||
|
{
|
||||||
|
IPathToken Current { get; }
|
||||||
|
|
||||||
|
bool Next(out IPathToken token);
|
||||||
|
|
||||||
|
bool Consume(PathTokenType type);
|
||||||
|
|
||||||
|
bool Peak(out IPathToken token);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
internal interface ITokenWriter
|
||||||
|
{
|
||||||
|
IPathToken Last { get; }
|
||||||
|
|
||||||
|
void Add(IPathToken token);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
[DebuggerDisplay("Type = {Type}, Arg = {Arg}")]
|
||||||
|
internal sealed class PathToken : IPathToken
|
||||||
|
{
|
||||||
|
public static readonly PathToken RootRef = new(PathTokenType.RootRef);
|
||||||
|
public static readonly PathToken CurrentRef = new(PathTokenType.CurrentRef);
|
||||||
|
|
||||||
|
public PathTokenType Type { get; }
|
||||||
|
|
||||||
|
public PathTokenOption Option { get; }
|
||||||
|
|
||||||
|
public PathToken(PathTokenType type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathToken(PathTokenType type, object arg, PathTokenOption option = PathTokenOption.None)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Arg = arg;
|
||||||
|
Option = option;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Arg { get; }
|
||||||
|
|
||||||
|
public T As<T>()
|
||||||
|
{
|
||||||
|
return Arg is T result ? result : default;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
internal enum PathTokenOption
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
CaseSensitive
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
internal enum PathTokenType
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: $
|
||||||
|
/// </summary>
|
||||||
|
RootRef,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: @
|
||||||
|
/// </summary>
|
||||||
|
CurrentRef,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: .Name
|
||||||
|
/// </summary>
|
||||||
|
DotSelector,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: [index]
|
||||||
|
/// </summary>
|
||||||
|
IndexSelector,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: [*]
|
||||||
|
/// </summary>
|
||||||
|
IndexWildSelector,
|
||||||
|
|
||||||
|
StartFilter,
|
||||||
|
ComparisonOperator,
|
||||||
|
Boolean,
|
||||||
|
EndFilter,
|
||||||
|
String,
|
||||||
|
Integer,
|
||||||
|
LogicalOperator,
|
||||||
|
|
||||||
|
StartGroup,
|
||||||
|
EndGroup,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: !
|
||||||
|
/// </summary>
|
||||||
|
NotOperator,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: ..
|
||||||
|
/// </summary>
|
||||||
|
DescendantSelector,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token: .*
|
||||||
|
/// </summary>
|
||||||
|
DotWildSelector,
|
||||||
|
|
||||||
|
ArraySliceSelector,
|
||||||
|
UnionIndexSelector,
|
||||||
|
UnionQuotedMemberSelector,
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime.ObjectPath;
|
||||||
|
|
||||||
|
internal sealed class TokenReader : ITokenReader
|
||||||
|
{
|
||||||
|
private readonly IPathToken[] _Tokens;
|
||||||
|
private readonly int _Last;
|
||||||
|
|
||||||
|
private int _Index;
|
||||||
|
|
||||||
|
public TokenReader(IPathToken[] tokens)
|
||||||
|
{
|
||||||
|
_Tokens = tokens;
|
||||||
|
_Last = tokens.Length - 1;
|
||||||
|
_Index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPathToken Current { get; private set; }
|
||||||
|
|
||||||
|
public bool Consume(PathTokenType type)
|
||||||
|
{
|
||||||
|
return Peak(out var token) && token.Type == type && Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Next(out IPathToken token)
|
||||||
|
{
|
||||||
|
token = null;
|
||||||
|
if (!Next())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
token = Current;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Next()
|
||||||
|
{
|
||||||
|
Current = _Index < _Last ? _Tokens[++_Index] : null;
|
||||||
|
return Current != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Peak(out IPathToken token)
|
||||||
|
{
|
||||||
|
token = _Index < _Last ? _Tokens[_Index + 1] : null;
|
||||||
|
return token != null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,194 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace PSRule.Runtime.ObjectPath;
|
|
||||||
|
|
||||||
internal enum PathTokenType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: $
|
|
||||||
/// </summary>
|
|
||||||
RootRef,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: @
|
|
||||||
/// </summary>
|
|
||||||
CurrentRef,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: .Name
|
|
||||||
/// </summary>
|
|
||||||
DotSelector,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: [index]
|
|
||||||
/// </summary>
|
|
||||||
IndexSelector,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: [*]
|
|
||||||
/// </summary>
|
|
||||||
IndexWildSelector,
|
|
||||||
|
|
||||||
StartFilter,
|
|
||||||
ComparisonOperator,
|
|
||||||
Boolean,
|
|
||||||
EndFilter,
|
|
||||||
String,
|
|
||||||
Integer,
|
|
||||||
LogicalOperator,
|
|
||||||
|
|
||||||
StartGroup,
|
|
||||||
EndGroup,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: !
|
|
||||||
/// </summary>
|
|
||||||
NotOperator,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: ..
|
|
||||||
/// </summary>
|
|
||||||
DescendantSelector,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Token: .*
|
|
||||||
/// </summary>
|
|
||||||
DotWildSelector,
|
|
||||||
|
|
||||||
ArraySliceSelector,
|
|
||||||
UnionIndexSelector,
|
|
||||||
UnionQuotedMemberSelector,
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum PathTokenOption
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
CaseSensitive
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum FilterOperator
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
// Comparison
|
|
||||||
Equal,
|
|
||||||
NotEqual,
|
|
||||||
LessOrEqual,
|
|
||||||
Less,
|
|
||||||
GreaterOrEqual,
|
|
||||||
Greater,
|
|
||||||
RegEx,
|
|
||||||
|
|
||||||
// Logical
|
|
||||||
Or,
|
|
||||||
And,
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface IPathToken
|
|
||||||
{
|
|
||||||
PathTokenType Type { get; }
|
|
||||||
|
|
||||||
PathTokenOption Option { get; }
|
|
||||||
|
|
||||||
object Arg { get; }
|
|
||||||
|
|
||||||
T As<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerDisplay("Type = {Type}, Arg = {Arg}")]
|
|
||||||
internal sealed class PathToken : IPathToken
|
|
||||||
{
|
|
||||||
public static readonly PathToken RootRef = new(PathTokenType.RootRef);
|
|
||||||
public static readonly PathToken CurrentRef = new(PathTokenType.CurrentRef);
|
|
||||||
|
|
||||||
public PathTokenType Type { get; }
|
|
||||||
|
|
||||||
public PathTokenOption Option { get; }
|
|
||||||
|
|
||||||
public PathToken(PathTokenType type)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PathToken(PathTokenType type, object arg, PathTokenOption option = PathTokenOption.None)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
Arg = arg;
|
|
||||||
Option = option;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Arg { get; }
|
|
||||||
|
|
||||||
public T As<T>()
|
|
||||||
{
|
|
||||||
return Arg is T result ? result : default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface ITokenWriter
|
|
||||||
{
|
|
||||||
IPathToken Last { get; }
|
|
||||||
|
|
||||||
void Add(IPathToken token);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface ITokenReader
|
|
||||||
{
|
|
||||||
IPathToken Current { get; }
|
|
||||||
|
|
||||||
bool Next(out IPathToken token);
|
|
||||||
|
|
||||||
bool Consume(PathTokenType type);
|
|
||||||
|
|
||||||
bool Peak(out IPathToken token);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class TokenReader : ITokenReader
|
|
||||||
{
|
|
||||||
private readonly IPathToken[] _Tokens;
|
|
||||||
private readonly int _Last;
|
|
||||||
|
|
||||||
private int _Index;
|
|
||||||
|
|
||||||
public TokenReader(IPathToken[] tokens)
|
|
||||||
{
|
|
||||||
_Tokens = tokens;
|
|
||||||
_Last = tokens.Length - 1;
|
|
||||||
_Index = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IPathToken Current { get; private set; }
|
|
||||||
|
|
||||||
public bool Consume(PathTokenType type)
|
|
||||||
{
|
|
||||||
return Peak(out var token) && token.Type == type && Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Next(out IPathToken token)
|
|
||||||
{
|
|
||||||
token = null;
|
|
||||||
if (!Next())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
token = Current;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Next()
|
|
||||||
{
|
|
||||||
Current = _Index < _Last ? _Tokens[++_Index] : null;
|
|
||||||
return Current != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Peak(out IPathToken token)
|
|
||||||
{
|
|
||||||
token = _Index < _Last ? _Tokens[_Index + 1] : null;
|
|
||||||
return token != null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,78 +3,6 @@
|
||||||
|
|
||||||
namespace PSRule.Runtime;
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of operand that is compared with the expression.
|
|
||||||
/// </summary>
|
|
||||||
public enum OperandKind
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Unknown.
|
|
||||||
/// </summary>
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An object path.
|
|
||||||
/// </summary>
|
|
||||||
Path = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The object target type.
|
|
||||||
/// </summary>
|
|
||||||
Type = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The object target name.
|
|
||||||
/// </summary>
|
|
||||||
Name = 3,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The object source information.
|
|
||||||
/// </summary>
|
|
||||||
Source = 4,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The target object itself.
|
|
||||||
/// </summary>
|
|
||||||
Target = 5,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A literal value or function.
|
|
||||||
/// </summary>
|
|
||||||
Value = 6,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The object scope.
|
|
||||||
/// </summary>
|
|
||||||
Scope = 7
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An operand that is compared with PSRule expressions.
|
|
||||||
/// </summary>
|
|
||||||
public interface IOperand
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The value of the operand.
|
|
||||||
/// </summary>
|
|
||||||
object Value { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of operand.
|
|
||||||
/// </summary>
|
|
||||||
OperandKind Kind { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The object path to the operand.
|
|
||||||
/// </summary>
|
|
||||||
string Path { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A logical prefix to add to the object path.
|
|
||||||
/// </summary>
|
|
||||||
string Prefix { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class Operand : IOperand
|
internal sealed class Operand : IOperand
|
||||||
{
|
{
|
||||||
private const string Dot = ".";
|
private const string Dot = ".";
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of operand that is compared with the expression.
|
||||||
|
/// </summary>
|
||||||
|
public enum OperandKind
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unknown.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An object path.
|
||||||
|
/// </summary>
|
||||||
|
Path = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The object target type.
|
||||||
|
/// </summary>
|
||||||
|
Type = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The object target name.
|
||||||
|
/// </summary>
|
||||||
|
Name = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The object source information.
|
||||||
|
/// </summary>
|
||||||
|
Source = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The target object itself.
|
||||||
|
/// </summary>
|
||||||
|
Target = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A literal value or function.
|
||||||
|
/// </summary>
|
||||||
|
Value = 6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The object scope.
|
||||||
|
/// </summary>
|
||||||
|
Scope = 7
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
internal static class RuleConditionHelper
|
||||||
|
{
|
||||||
|
private static readonly RuleConditionResult Empty = new(pass: 0, count: 0, hadErrors: false);
|
||||||
|
|
||||||
|
internal static RuleConditionResult Create(IEnumerable<object> value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return Empty;
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
var pass = 0;
|
||||||
|
var hasErrors = false;
|
||||||
|
foreach (var v in value)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if (v == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var baseObject = ExpressionHelpers.GetBaseObject(v);
|
||||||
|
if (!(TryAssertResult(baseObject, out var result) || TryBoolean(baseObject, out result)))
|
||||||
|
{
|
||||||
|
RunspaceContext.CurrentThread.ErrorInvaildRuleResult();
|
||||||
|
hasErrors = true;
|
||||||
|
}
|
||||||
|
else if (result)
|
||||||
|
{
|
||||||
|
pass++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new RuleConditionResult(pass, count, hasErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryBoolean(object o, out bool result)
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
if (o is not bool bresult)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
result = bresult;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryAssertResult(object o, out bool result)
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
if (o is not AssertResult assert)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
result = assert.Result;
|
||||||
|
|
||||||
|
// Complete results
|
||||||
|
if (RunspaceContext.CurrentThread.IsScope(RunspaceScope.Rule))
|
||||||
|
assert.Complete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,64 +5,6 @@ using PSRule.Definitions;
|
||||||
|
|
||||||
namespace PSRule.Runtime;
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
internal static class RuleConditionHelper
|
|
||||||
{
|
|
||||||
private static readonly RuleConditionResult Empty = new(pass: 0, count: 0, hadErrors: false);
|
|
||||||
|
|
||||||
internal static RuleConditionResult Create(IEnumerable<object> value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
return Empty;
|
|
||||||
|
|
||||||
var count = 0;
|
|
||||||
var pass = 0;
|
|
||||||
var hasErrors = false;
|
|
||||||
foreach (var v in value)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (v == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var baseObject = ExpressionHelpers.GetBaseObject(v);
|
|
||||||
if (!(TryAssertResult(baseObject, out var result) || TryBoolean(baseObject, out result)))
|
|
||||||
{
|
|
||||||
RunspaceContext.CurrentThread.ErrorInvaildRuleResult();
|
|
||||||
hasErrors = true;
|
|
||||||
}
|
|
||||||
else if (result)
|
|
||||||
{
|
|
||||||
pass++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new RuleConditionResult(pass, count, hasErrors);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryBoolean(object o, out bool result)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
if (o is not bool bresult)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
result = bresult;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryAssertResult(object o, out bool result)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
if (o is not AssertResult assert)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
result = assert.Result;
|
|
||||||
|
|
||||||
// Complete results
|
|
||||||
if (RunspaceContext.CurrentThread.IsScope(RunspaceScope.Rule))
|
|
||||||
assert.Complete();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class RuleConditionResult : IConditionResult
|
internal sealed class RuleConditionResult : IConditionResult
|
||||||
{
|
{
|
||||||
internal RuleConditionResult(int pass, int count, bool hadErrors)
|
internal RuleConditionResult(int pass, int count, bool hadErrors)
|
||||||
|
|
|
@ -14,42 +14,6 @@ using static PSRule.Pipeline.PipelineContext;
|
||||||
|
|
||||||
namespace PSRule.Runtime;
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The available language scope types.
|
|
||||||
/// </summary>
|
|
||||||
[Flags]
|
|
||||||
public enum RunspaceScope
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
Source = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executing a rule.
|
|
||||||
/// </summary>
|
|
||||||
Rule = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executing a rule precondition.
|
|
||||||
/// </summary>
|
|
||||||
Precondition = 4,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Execution is currently parsing YAML objects.
|
|
||||||
/// </summary>
|
|
||||||
Resource = 8,
|
|
||||||
|
|
||||||
ConventionBegin = 16,
|
|
||||||
ConventionProcess = 32,
|
|
||||||
ConventionEnd = 64,
|
|
||||||
ConventionInitialize = 128,
|
|
||||||
|
|
||||||
Convention = ConventionInitialize | ConventionBegin | ConventionProcess | ConventionEnd,
|
|
||||||
Target = Rule | Precondition | ConventionBegin | ConventionProcess,
|
|
||||||
Runtime = Rule | Precondition | Convention,
|
|
||||||
All = Source | Rule | Precondition | Resource | Convention,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A context for a PSRule runspace.
|
/// A context for a PSRule runspace.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
namespace PSRule.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The available language scope types.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum RunspaceScope
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
Source = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executing a rule.
|
||||||
|
/// </summary>
|
||||||
|
Rule = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executing a rule precondition.
|
||||||
|
/// </summary>
|
||||||
|
Precondition = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execution is currently parsing YAML objects.
|
||||||
|
/// </summary>
|
||||||
|
Resource = 8,
|
||||||
|
|
||||||
|
ConventionBegin = 16,
|
||||||
|
ConventionProcess = 32,
|
||||||
|
ConventionEnd = 64,
|
||||||
|
ConventionInitialize = 128,
|
||||||
|
|
||||||
|
Convention = ConventionInitialize | ConventionBegin | ConventionProcess | ConventionEnd,
|
||||||
|
Target = Rule | Precondition | ConventionBegin | ConventionProcess,
|
||||||
|
Runtime = Rule | Precondition | Convention,
|
||||||
|
All = Source | Rule | Precondition | Resource | Convention,
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using PSRule.Definitions;
|
using PSRule.Definitions;
|
||||||
using PSRule.Definitions.Rules;
|
using PSRule.Definitions.Rules;
|
||||||
|
@ -10,24 +11,24 @@ namespace PSRule;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define tests to validate <see cref="RuleFilter"/>.
|
/// Define tests to validate <see cref="RuleFilter"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class RuleFilterTests
|
public sealed partial class RuleFilterTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MatchInclude()
|
public void MatchInclude()
|
||||||
{
|
{
|
||||||
var filter = new RuleFilter(new string[] { "rule1", "rule2" }, null, null, null, null);
|
var filter = new RuleFilter(["rule1", "rule2"], null, null, null, null);
|
||||||
Assert.True(filter.Match("rule1", null, null));
|
Assert.True(filter.Match(GetResource("rule1")));
|
||||||
Assert.True(filter.Match("Rule2", null, null));
|
Assert.True(filter.Match(GetResource("Rule2")));
|
||||||
Assert.False(filter.Match("rule3", null, null));
|
Assert.False(filter.Match(GetResource("rule3")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MatchExclude()
|
public void MatchExclude()
|
||||||
{
|
{
|
||||||
var filter = new RuleFilter(null, null, new string[] { "rule3" }, null, null);
|
var filter = new RuleFilter(null, null, ["rule3"], null, null);
|
||||||
Assert.True(filter.Match("rule1", null, null));
|
Assert.True(filter.Match(GetResource("rule1")));
|
||||||
Assert.True(filter.Match("rule2", null, null));
|
Assert.True(filter.Match(GetResource("rule2")));
|
||||||
Assert.False(filter.Match("Rule3", null, null));
|
Assert.False(filter.Match(GetResource("Rule3")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -45,12 +46,23 @@ public sealed class RuleFilterTests
|
||||||
|
|
||||||
// Check basic match
|
// Check basic match
|
||||||
resourceTags["category"] = "group2";
|
resourceTags["category"] = "group2";
|
||||||
Assert.True(filter.Match("rule", ResourceTags.FromHashtable(resourceTags), null));
|
Assert.True(filter.Match(GetResource("rule", ResourceTags.FromHashtable(resourceTags))));
|
||||||
resourceTags["category"] = "group1";
|
resourceTags["category"] = "group1";
|
||||||
Assert.True(filter.Match("rule", ResourceTags.FromHashtable(resourceTags), null));
|
Assert.True(filter.Match(GetResource("rule", ResourceTags.FromHashtable(resourceTags))));
|
||||||
resourceTags["category"] = "group3";
|
resourceTags["category"] = "group3";
|
||||||
Assert.False(filter.Match("rule", ResourceTags.FromHashtable(resourceTags), null));
|
Assert.False(filter.Match(GetResource("rule", ResourceTags.FromHashtable(resourceTags))));
|
||||||
Assert.False(filter.Match("rule", null, null));
|
Assert.False(filter.Match(GetResource("rule")));
|
||||||
|
|
||||||
|
// Include local
|
||||||
|
filter = new RuleFilter(null, tag, null, true, null);
|
||||||
|
resourceTags["category"] = "group1";
|
||||||
|
Assert.True(filter.Match(GetResource("module1\\rule", ResourceTags.FromHashtable(resourceTags))));
|
||||||
|
resourceTags["category"] = "group3";
|
||||||
|
Assert.False(filter.Match(GetResource("module1\\rule", ResourceTags.FromHashtable(resourceTags))));
|
||||||
|
resourceTags["category"] = "group3";
|
||||||
|
Assert.True(filter.Match(GetResource(".\\rule", ResourceTags.FromHashtable(resourceTags))));
|
||||||
|
Assert.False(filter.Match(GetResource("module1\\rule")));
|
||||||
|
Assert.True(filter.Match(GetResource(".\\rule")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -62,19 +74,28 @@ public sealed class RuleFilterTests
|
||||||
// Create a filter
|
// Create a filter
|
||||||
var labels = new ResourceLabels
|
var labels = new ResourceLabels
|
||||||
{
|
{
|
||||||
["framework.v1/control"] = new string[] { "c-1", "c-2" }
|
["framework.v1/control"] = ["c-1", "c-2"]
|
||||||
};
|
};
|
||||||
var filter = new RuleFilter(null, null, null, null, labels);
|
var filter = new RuleFilter(null, null, null, null, labels);
|
||||||
|
|
||||||
resourceLabels["framework.v1/control"] = new string[] { "c-2", "c-1" };
|
resourceLabels["framework.v1/control"] = new string[] { "c-2", "c-1" };
|
||||||
Assert.True(filter.Match("rule", null, ResourceLabels.FromHashtable(resourceLabels)));
|
Assert.True(filter.Match(GetResource("rule", null, ResourceLabels.FromHashtable(resourceLabels))));
|
||||||
resourceLabels["framework.v1/control"] = new string[] { "c-3", "c-1" };
|
resourceLabels["framework.v1/control"] = new string[] { "c-3", "c-1" };
|
||||||
Assert.True(filter.Match("rule", null, ResourceLabels.FromHashtable(resourceLabels)));
|
Assert.True(filter.Match(GetResource("rule", null, ResourceLabels.FromHashtable(resourceLabels))));
|
||||||
resourceLabels["framework.v1/control"] = new string[] { "c-1", "c-3" };
|
resourceLabels["framework.v1/control"] = new string[] { "c-1", "c-3" };
|
||||||
Assert.True(filter.Match("rule", null, ResourceLabels.FromHashtable(resourceLabels)));
|
Assert.True(filter.Match(GetResource("rule", null, ResourceLabels.FromHashtable(resourceLabels))));
|
||||||
resourceLabels["framework.v1/control"] = new string[] { "c-3", "c-4" };
|
resourceLabels["framework.v1/control"] = new string[] { "c-3", "c-4" };
|
||||||
Assert.False(filter.Match("rule", null, ResourceLabels.FromHashtable(resourceLabels)));
|
Assert.False(filter.Match(GetResource("rule", null, ResourceLabels.FromHashtable(resourceLabels))));
|
||||||
resourceLabels["framework.v1/control"] = System.Array.Empty<string>();
|
resourceLabels["framework.v1/control"] = Array.Empty<string>();
|
||||||
Assert.False(filter.Match("rule", null, ResourceLabels.FromHashtable(resourceLabels)));
|
Assert.False(filter.Match(GetResource("rule", null, ResourceLabels.FromHashtable(resourceLabels))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
|
||||||
|
private static IResource GetResource(string id, ResourceTags resourceTags = null, ResourceLabels resourceLabels = null)
|
||||||
|
{
|
||||||
|
return new TestResourceName(ResourceId.Parse(id), resourceTags, resourceLabels);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Helper Methods
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using PSRule.Definitions;
|
||||||
|
using PSRule.Pipeline;
|
||||||
|
|
||||||
|
namespace PSRule;
|
||||||
|
|
||||||
|
internal sealed class TestResourceName : IResource
|
||||||
|
{
|
||||||
|
public TestResourceName(ResourceId id, ResourceTags resourceTags = null, ResourceLabels resourceLabels = null)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = Id.Name;
|
||||||
|
Module = Id.Scope != null && Id.Scope != "." ? Id.Scope : null;
|
||||||
|
Tags = resourceTags ?? new ResourceTags();
|
||||||
|
Labels = resourceLabels ?? new ResourceLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceKind Kind => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public string ApiVersion => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public ResourceId? Ref => null;
|
||||||
|
|
||||||
|
public ResourceId[] Alias => Array.Empty<ResourceId>();
|
||||||
|
|
||||||
|
public ResourceTags Tags { get; }
|
||||||
|
|
||||||
|
public ResourceLabels Labels { get; }
|
||||||
|
|
||||||
|
public ResourceFlags Flags => ResourceFlags.None;
|
||||||
|
|
||||||
|
public ISourceExtent Extent => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public IResourceHelpInfo Info => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public ResourceId Id { get; }
|
||||||
|
|
||||||
|
public string SourcePath => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public string Module { get; }
|
||||||
|
|
||||||
|
public SourceFile Source => throw new System.NotImplementedException();
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
global using Moq;
|
||||||
global using Xunit;
|
global using Xunit;
|
||||||
global using Assert = Xunit.Assert;
|
global using Assert = Xunit.Assert;
|
||||||
global using Moq;
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче