diff --git a/src/PSRule.Badges/BadgeException.cs b/src/PSRule.Badges/BadgeException.cs new file mode 100644 index 0000000000..5b9ccc7441 --- /dev/null +++ b/src/PSRule.Badges/BadgeException.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Runtime.Serialization; + +namespace PSRule.Badges; + +/// +/// A generic exception for handling of badges. +/// +[Serializable] +internal class BadgeException : Exception +{ + public BadgeException() + { + } + + public BadgeException(string message) : base(message) + { + } + + public BadgeException(string message, Exception innerException) : base(message, innerException) + { + } + + protected BadgeException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +} diff --git a/src/PSRule.Badges/BadgeResources.cs b/src/PSRule.Badges/BadgeResources.cs index fe87f9e6e1..55dbbb5eb6 100644 --- a/src/PSRule.Badges/BadgeResources.cs +++ b/src/PSRule.Badges/BadgeResources.cs @@ -1,8 +1,9 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Reflection; using Newtonsoft.Json; +using PSRule.Badges.Resources; namespace PSRule.Badges; @@ -13,8 +14,8 @@ internal static class BadgeResources { private const string DEFAULT_CULTURE_RESOURCE = "PSRule.Badges.Resources.en.json"; - private static char[] _Char; - private static double[] _Width; + private static char[]? _Char; + private static double[]? _Width; /// /// Load pre-calculated widths for characters. @@ -29,6 +30,9 @@ private static void Load() using var reader = new StreamReader(stream); var json = reader.ReadToEnd(); var d = JsonConvert.DeserializeObject(json); + if (d == null) + throw new BadgeException(Messages.LoadException); + _Char = new char[d.Length]; _Width = new double[d.Length]; for (var i = 0; i < d.Length; i++) @@ -52,6 +56,8 @@ private static double GetWidth(char c) /// private static double Find(char c) { + if (_Char == null || _Width == null) return 0d; + var index = Array.BinarySearch(_Char, c); return index >= 0 ? _Width[index] : 0d; } @@ -59,7 +65,7 @@ private static double Find(char c) public static double Measure(string s) { var length = 0d; - for (var i = 0; i < s.Length; i++) + for (var i = 0; s != null && i < s.Length; i++) length += GetWidth(s[i]); return length; diff --git a/src/PSRule.Badges/PSRule.Badges.csproj b/src/PSRule.Badges/PSRule.Badges.csproj index 4dc36456d2..caba5918ac 100644 --- a/src/PSRule.Badges/PSRule.Badges.csproj +++ b/src/PSRule.Badges/PSRule.Badges.csproj @@ -6,6 +6,7 @@ Microsoft.PSRule.Badges Library {309bed8b-4e60-4c42-a2b4-37a2e7ebef3f} + enable README.md True @@ -22,4 +23,19 @@ + + + Messages.resx + True + True + + + + + + Messages.Designer.cs + ResXFileCodeGenerator + + + diff --git a/src/PSRule.Badges/Resources/Messages.Designer.cs b/src/PSRule.Badges/Resources/Messages.Designer.cs new file mode 100644 index 0000000000..d15641d323 --- /dev/null +++ b/src/PSRule.Badges/Resources/Messages.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PSRule.Badges.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Messages { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Messages() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PSRule.Badges.Resources.Messages", typeof(Messages).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Failed to load badge resources.. + /// + internal static string LoadException { + get { + return ResourceManager.GetString("LoadException", resourceCulture); + } + } + } +} diff --git a/src/PSRule.Badges/Resources/Messages.resx b/src/PSRule.Badges/Resources/Messages.resx new file mode 100644 index 0000000000..f3f958d755 --- /dev/null +++ b/src/PSRule.Badges/Resources/Messages.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Failed to load badge resources. + + \ No newline at end of file diff --git a/src/PSRule/Pipeline/GetBaselinePipeline.cs b/src/PSRule/Pipeline/GetBaselinePipeline.cs index 1e7a664c86..27cb9a24c7 100644 --- a/src/PSRule/Pipeline/GetBaselinePipeline.cs +++ b/src/PSRule/Pipeline/GetBaselinePipeline.cs @@ -14,7 +14,7 @@ internal sealed class GetBaselinePipeline : RulePipeline internal GetBaselinePipeline( PipelineContext pipeline, Source[] source, - PipelineReader reader, + PipelineInputStream reader, IPipelineWriter writer, IResourceFilter filter ) diff --git a/src/PSRule/Pipeline/GetRuleHelpPipeline.cs b/src/PSRule/Pipeline/GetRuleHelpPipeline.cs index 4d5bc3f268..3dfe489fcb 100644 --- a/src/PSRule/Pipeline/GetRuleHelpPipeline.cs +++ b/src/PSRule/Pipeline/GetRuleHelpPipeline.cs @@ -176,7 +176,7 @@ protected override PipelineWriter PrepareWriter() internal sealed class GetRuleHelpPipeline : RulePipeline, IPipeline { - internal GetRuleHelpPipeline(PipelineContext pipeline, Source[] source, PipelineReader reader, IPipelineWriter writer) + internal GetRuleHelpPipeline(PipelineContext pipeline, Source[] source, PipelineInputStream reader, IPipelineWriter writer) : base(pipeline, source, reader, writer) { // Do nothing diff --git a/src/PSRule/Pipeline/GetRulePipeline.cs b/src/PSRule/Pipeline/GetRulePipeline.cs index b045c2622a..cc9bdb32df 100644 --- a/src/PSRule/Pipeline/GetRulePipeline.cs +++ b/src/PSRule/Pipeline/GetRulePipeline.cs @@ -12,7 +12,7 @@ internal sealed class GetRulePipeline : RulePipeline, IPipeline internal GetRulePipeline( PipelineContext pipeline, Source[] source, - PipelineReader reader, + PipelineInputStream reader, IPipelineWriter writer, bool includeDependencies ) diff --git a/src/PSRule/Pipeline/GetTargetPipeline.cs b/src/PSRule/Pipeline/GetTargetPipeline.cs index 34723d4ea1..1683ae32a6 100644 --- a/src/PSRule/Pipeline/GetTargetPipeline.cs +++ b/src/PSRule/Pipeline/GetTargetPipeline.cs @@ -72,7 +72,7 @@ public override IPipeline Build(IPipelineWriter writer = null) } /// - protected override PipelineReader PrepareReader() + protected override PipelineInputStream PrepareReader() { if (!string.IsNullOrEmpty(Option.Input.ObjectPath)) { @@ -124,7 +124,7 @@ protected override PipelineReader PrepareReader() return PipelineReceiverActions.DetectInputFormat(sourceObject, next); }); } - return new PipelineReader(VisitTargetObject, _InputPath, GetInputObjectSourceFilter()); + return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter()); } } @@ -133,7 +133,7 @@ protected override PipelineReader PrepareReader() /// internal sealed class GetTargetPipeline : RulePipeline { - internal GetTargetPipeline(PipelineContext context, PipelineReader reader, IPipelineWriter writer) + internal GetTargetPipeline(PipelineContext context, PipelineInputStream reader, IPipelineWriter writer) : base(context, null, reader, writer) { } public override void Process(PSObject sourceObject) diff --git a/src/PSRule/Pipeline/InvokePipelineBuilder.cs b/src/PSRule/Pipeline/InvokePipelineBuilder.cs index 6554ec9c69..3e07256b31 100644 --- a/src/PSRule/Pipeline/InvokePipelineBuilder.cs +++ b/src/PSRule/Pipeline/InvokePipelineBuilder.cs @@ -149,7 +149,7 @@ private static bool IsBlocked(string path) } } - protected override PipelineReader PrepareReader() + protected override PipelineInputStream PrepareReader() { if (!string.IsNullOrEmpty(Option.Input.ObjectPath)) { @@ -201,7 +201,7 @@ protected override PipelineReader PrepareReader() return PipelineReceiverActions.DetectInputFormat(sourceObject, next); }); } - return new PipelineReader(VisitTargetObject, _InputPath, GetInputObjectSourceFilter()); + return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter()); } } diff --git a/src/PSRule/Pipeline/PipelineBuilder.cs b/src/PSRule/Pipeline/PipelineBuilder.cs index 31ab502335..e4ee3d3ae2 100644 --- a/src/PSRule/Pipeline/PipelineBuilder.cs +++ b/src/PSRule/Pipeline/PipelineBuilder.cs @@ -468,9 +468,9 @@ protected string ResolveBaselineGroup(string name) return baselines[0]; } - protected virtual PipelineReader PrepareReader() + protected virtual PipelineInputStream PrepareReader() { - return new PipelineReader(null, null, GetInputObjectSourceFilter()); + return new PipelineInputStream(null, null, GetInputObjectSourceFilter()); } protected virtual PipelineWriter PrepareWriter() diff --git a/src/PSRule/Pipeline/PipelineContext.cs b/src/PSRule/Pipeline/PipelineContext.cs index 1e20e2418f..c601b58552 100644 --- a/src/PSRule/Pipeline/PipelineContext.cs +++ b/src/PSRule/Pipeline/PipelineContext.cs @@ -53,7 +53,7 @@ internal sealed class PipelineContext : IDisposable, IBindingContext internal readonly Dictionary Selector; internal readonly List SuppressionGroup; internal readonly IHostContext HostContext; - internal readonly PipelineReader Reader; + internal readonly PipelineInputStream Reader; internal readonly BindTargetMethod BindTargetName; internal readonly BindTargetMethod BindTargetType; internal readonly BindTargetMethod BindField; @@ -65,7 +65,7 @@ internal sealed class PipelineContext : IDisposable, IBindingContext public System.Security.Cryptography.HashAlgorithm ObjectHashAlgorithm { get; } - private PipelineContext(PSRuleOption option, IHostContext hostContext, PipelineReader reader, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, OptionContextBuilder optionBuilder, IList unresolved) + private PipelineContext(PSRuleOption option, IHostContext hostContext, PipelineInputStream reader, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, OptionContextBuilder optionBuilder, IList unresolved) { _OptionBuilder = optionBuilder; Option = option; @@ -90,7 +90,7 @@ private PipelineContext(PSRuleOption option, IHostContext hostContext, PipelineR _DefaultOptionContext = _OptionBuilder?.Build(null); } - public static PipelineContext New(PSRuleOption option, IHostContext hostContext, PipelineReader reader, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, OptionContextBuilder optionBuilder, IList unresolved) + public static PipelineContext New(PSRuleOption option, IHostContext hostContext, PipelineInputStream reader, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, OptionContextBuilder optionBuilder, IList unresolved) { var context = new PipelineContext(option, hostContext, reader, bindTargetName, bindTargetType, bindField, optionBuilder, unresolved); CurrentThread = context; diff --git a/src/PSRule/Pipeline/PipelineReader.cs b/src/PSRule/Pipeline/PipelineInputStream.cs similarity index 81% rename from src/PSRule/Pipeline/PipelineReader.cs rename to src/PSRule/Pipeline/PipelineInputStream.cs index 0c66466aa3..64497dca61 100644 --- a/src/PSRule/Pipeline/PipelineReader.cs +++ b/src/PSRule/Pipeline/PipelineInputStream.cs @@ -6,14 +6,17 @@ namespace PSRule.Pipeline; -internal sealed class PipelineReader +/// +/// A stream of input objects that will be evaluated. +/// +internal sealed class PipelineInputStream { private readonly VisitTargetObject _Input; private readonly InputPathBuilder _InputPath; private readonly PathFilter _InputFilter; private readonly ConcurrentQueue _Queue; - public PipelineReader(VisitTargetObject input, InputPathBuilder inputPath, PathFilter inputFilter) + public PipelineInputStream(VisitTargetObject input, InputPathBuilder inputPath, PathFilter inputFilter) { _Input = input; _InputPath = inputPath; @@ -25,6 +28,12 @@ public PipelineReader(VisitTargetObject input, InputPathBuilder inputPath, PathF public bool IsEmpty => _Queue.IsEmpty; + /// + /// Add a new object into the stream. + /// + /// An object to process. + /// A pre-bound type. + /// Determines if expansion is skipped. public void Enqueue(PSObject sourceObject, string targetType = null, bool skipExpansion = false) { if (sourceObject == null) @@ -46,6 +55,9 @@ public void Enqueue(PSObject sourceObject, string targetType = null, bool skipEx EnqueueInternal(item); } + /// + /// Get the next object in the stream. + /// public bool TryDequeue(out TargetObject sourceObject) { return _Queue.TryDequeue(out sourceObject); diff --git a/src/PSRule/Pipeline/RulePipeline.cs b/src/PSRule/Pipeline/RulePipeline.cs index 207ff27ed0..653c61ea5c 100644 --- a/src/PSRule/Pipeline/RulePipeline.cs +++ b/src/PSRule/Pipeline/RulePipeline.cs @@ -11,13 +11,13 @@ internal abstract class RulePipeline : IPipeline protected readonly PipelineContext Pipeline; protected readonly RunspaceContext Context; protected readonly Source[] Source; - protected readonly PipelineReader Reader; + protected readonly PipelineInputStream Reader; protected readonly IPipelineWriter Writer; // Track whether Dispose has been called. private bool _Disposed; - protected RulePipeline(PipelineContext context, Source[] source, PipelineReader reader, IPipelineWriter writer) + protected RulePipeline(PipelineContext context, Source[] source, PipelineInputStream reader, IPipelineWriter writer) { Result = new DefaultPipelineResult(writer); Pipeline = context; diff --git a/tests/PSRule.Tests/PipelineTests.cs b/tests/PSRule.Tests/PipelineTests.cs index d02c7b67f7..23a7395ef0 100644 --- a/tests/PSRule.Tests/PipelineTests.cs +++ b/tests/PSRule.Tests/PipelineTests.cs @@ -181,7 +181,7 @@ public void PipelineWithInvariantCulture() Environment.UseCurrentCulture(CultureInfo.InvariantCulture); var context = PipelineContext.New(GetOption(), null, null, null, null, null, new OptionContextBuilder(), null); var writer = new TestWriter(GetOption()); - var pipeline = new GetRulePipeline(context, GetSource(), new PipelineReader(null, null, null), writer, false); + var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null), writer, false); try { pipeline.Begin(); @@ -203,7 +203,7 @@ public void PipelineWithInvariantCultureDisabled() option.Execution.InvariantCulture = ExecutionActionPreference.Ignore; var context = PipelineContext.New(option, null, null, null, null, null, new OptionContextBuilder(), null); var writer = new TestWriter(option); - var pipeline = new GetRulePipeline(context, GetSource(), new PipelineReader(null, null, null), writer, false); + var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null), writer, false); try { pipeline.Begin();