зеркало из https://github.com/microsoft/PSRule.git
Refactoring (#1852)
This commit is contained in:
Родитель
3b6be55376
Коммит
cdc08bb115
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System.CommandLine.Invocation;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Options;
|
||||
|
||||
namespace PSRule.CommandLine;
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// 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;
|
||||
|
||||
/// <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>
|
||||
/// A badge builder that implements the Badge API within PSRule.
|
||||
/// </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)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -2,19 +2,9 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System.ComponentModel;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options that affect property binding of TargetName and TargetType.
|
||||
/// </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.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using PSRule.Converters.Yaml;
|
||||
using PSRule.Definitions.Rules;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Runtime;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
using YamlDotNet.Serialization.NodeDeserializers;
|
||||
|
||||
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>
|
||||
/// A base class for resources.
|
||||
/// </summary>
|
||||
|
@ -550,132 +85,3 @@ public abstract class Resource<TSpec> where TSpec : Spec, new()
|
|||
/// </summary>
|
||||
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;
|
||||
|
||||
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>
|
||||
/// Matches if the RuleId is contained or any tag is matched
|
||||
/// </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;
|
||||
|
||||
internal enum MarkdownReaderMode
|
||||
{
|
||||
None,
|
||||
|
||||
List
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stateful markdown reader.
|
||||
/// </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);
|
||||
|
||||
[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}")]
|
||||
internal sealed class MarkdownStream
|
||||
{
|
||||
|
|
|
@ -5,70 +5,6 @@ using System.Diagnostics;
|
|||
|
||||
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}")]
|
||||
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;
|
||||
|
||||
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}")]
|
||||
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.Runspaces;
|
||||
using PSRule.Commands;
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
/// <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 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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define the structure for the PSRule lock file.
|
||||
/// By default, this file is <c>ps-rule.lock.json</c>.
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
using PSRule.Emitters;
|
||||
|
||||
namespace PSRule.Pipeline.Emitters
|
||||
{
|
||||
/// <summary>
|
||||
/// A chain of emitters.
|
||||
/// </summary>
|
||||
internal delegate bool EmitterChain(IEmitterContext context, object o, Type type);
|
||||
}
|
||||
namespace PSRule.Pipeline.Emitters;
|
||||
|
||||
/// <summary>
|
||||
/// A chain of emitters.
|
||||
/// </summary>
|
||||
internal delegate bool EmitterChain(IEmitterContext context, object o, Type type);
|
||||
|
|
|
@ -4,51 +4,50 @@
|
|||
using System.Diagnostics;
|
||||
using PSRule.Data;
|
||||
|
||||
namespace PSRule.Pipeline.Emitters
|
||||
namespace PSRule.Pipeline.Emitters;
|
||||
|
||||
[DebuggerDisplay("{Path}")]
|
||||
internal sealed class InternalFileInfo : IFileInfo, IDisposable
|
||||
{
|
||||
[DebuggerDisplay("{Path}")]
|
||||
internal sealed class InternalFileInfo : IFileInfo, IDisposable
|
||||
private FileStream _Stream;
|
||||
private bool _Disposed;
|
||||
|
||||
public InternalFileInfo(string path, string extension)
|
||||
{
|
||||
private FileStream _Stream;
|
||||
private bool _Disposed;
|
||||
Path = path;
|
||||
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;
|
||||
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)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_Stream.Dispose();
|
||||
}
|
||||
_Disposed = true;
|
||||
_Stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
_Disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
_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)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_Stream?.Dispose();
|
||||
}
|
||||
_Disposed = true;
|
||||
_Stream?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
_Disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
||||
_Settings = new JsonSerializerSettings
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
_Deserializer = JsonSerializer.CreateDefault(_Settings); // Think about caching this.
|
||||
_Deserializer.Converters.Add(new PSObjectArrayJsonConverter(null));
|
||||
|
@ -88,13 +87,13 @@ internal sealed class JsonEmitter : FileEmitter
|
|||
VisitItems(context, value, null);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ internal sealed class YamlEmitter : FileEmitter
|
|||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
|
|
@ -9,115 +9,6 @@ using PSRule.Rules;
|
|||
|
||||
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>
|
||||
/// A base class for a formatter.
|
||||
/// </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;
|
||||
|
||||
/// <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>
|
||||
/// Output view helper class for rule help.
|
||||
/// </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;
|
||||
|
||||
/// <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>
|
||||
/// A generic interface for diagnostic logging within PSRule.
|
||||
/// </summary>
|
||||
|
@ -45,6 +26,6 @@ internal interface ILogger
|
|||
/// Write an error from an exception.
|
||||
/// </summary>
|
||||
/// <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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <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}")]
|
||||
internal sealed class LanguageScope : ILanguageScope
|
||||
{
|
||||
|
@ -225,102 +177,3 @@ internal sealed class LanguageScope : ILanguageScope
|
|||
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;
|
||||
|
||||
/// <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>
|
||||
/// A token for expressing a path through a tree of fields.
|
||||
/// </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;
|
||||
|
||||
/// <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
|
||||
{
|
||||
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;
|
||||
|
||||
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 RuleConditionResult(int pass, int count, bool hadErrors)
|
||||
|
|
|
@ -14,42 +14,6 @@ using static PSRule.Pipeline.PipelineContext;
|
|||
|
||||
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>
|
||||
/// A context for a PSRule runspace.
|
||||
/// </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.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Definitions.Rules;
|
||||
|
@ -10,24 +11,24 @@ namespace PSRule;
|
|||
/// <summary>
|
||||
/// Define tests to validate <see cref="RuleFilter"/>.
|
||||
/// </summary>
|
||||
public sealed class RuleFilterTests
|
||||
public sealed partial class RuleFilterTests
|
||||
{
|
||||
[Fact]
|
||||
public void MatchInclude()
|
||||
{
|
||||
var filter = new RuleFilter(new string[] { "rule1", "rule2" }, null, null, null, null);
|
||||
Assert.True(filter.Match("rule1", null, null));
|
||||
Assert.True(filter.Match("Rule2", null, null));
|
||||
Assert.False(filter.Match("rule3", null, null));
|
||||
var filter = new RuleFilter(["rule1", "rule2"], null, null, null, null);
|
||||
Assert.True(filter.Match(GetResource("rule1")));
|
||||
Assert.True(filter.Match(GetResource("Rule2")));
|
||||
Assert.False(filter.Match(GetResource("rule3")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchExclude()
|
||||
{
|
||||
var filter = new RuleFilter(null, null, new string[] { "rule3" }, null, null);
|
||||
Assert.True(filter.Match("rule1", null, null));
|
||||
Assert.True(filter.Match("rule2", null, null));
|
||||
Assert.False(filter.Match("Rule3", null, null));
|
||||
var filter = new RuleFilter(null, null, ["rule3"], null, null);
|
||||
Assert.True(filter.Match(GetResource("rule1")));
|
||||
Assert.True(filter.Match(GetResource("rule2")));
|
||||
Assert.False(filter.Match(GetResource("Rule3")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -45,12 +46,23 @@ public sealed class RuleFilterTests
|
|||
|
||||
// Check basic match
|
||||
resourceTags["category"] = "group2";
|
||||
Assert.True(filter.Match("rule", ResourceTags.FromHashtable(resourceTags), null));
|
||||
Assert.True(filter.Match(GetResource("rule", ResourceTags.FromHashtable(resourceTags))));
|
||||
resourceTags["category"] = "group1";
|
||||
Assert.True(filter.Match("rule", ResourceTags.FromHashtable(resourceTags), null));
|
||||
Assert.True(filter.Match(GetResource("rule", ResourceTags.FromHashtable(resourceTags))));
|
||||
resourceTags["category"] = "group3";
|
||||
Assert.False(filter.Match("rule", ResourceTags.FromHashtable(resourceTags), null));
|
||||
Assert.False(filter.Match("rule", null, null));
|
||||
Assert.False(filter.Match(GetResource("rule", ResourceTags.FromHashtable(resourceTags))));
|
||||
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]
|
||||
|
@ -62,19 +74,28 @@ public sealed class RuleFilterTests
|
|||
// Create a filter
|
||||
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);
|
||||
|
||||
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" };
|
||||
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" };
|
||||
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" };
|
||||
Assert.False(filter.Match("rule", null, ResourceLabels.FromHashtable(resourceLabels)));
|
||||
resourceLabels["framework.v1/control"] = System.Array.Empty<string>();
|
||||
Assert.False(filter.Match("rule", null, ResourceLabels.FromHashtable(resourceLabels)));
|
||||
Assert.False(filter.Match(GetResource("rule", null, ResourceLabels.FromHashtable(resourceLabels))));
|
||||
resourceLabels["framework.v1/control"] = Array.Empty<string>();
|
||||
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.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
global using Moq;
|
||||
global using Xunit;
|
||||
global using Assert = Xunit.Assert;
|
||||
global using Moq;
|
||||
|
|
Загрузка…
Ссылка в новой задаче