From 21ba5c7d4fecd7d05e2a57911efcf4af144508f8 Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Wed, 8 Aug 2018 22:27:08 +0200 Subject: [PATCH 1/4] Initial partial implementation --- PSModuleDevelopment/PSModuleDevelopment.psd1 | 2 +- .../bin/PSModuleDevelopment.dll | Bin 18432 -> 18432 bytes .../bin/PSModuleDevelopment.pdb | Bin 56832 -> 56832 bytes .../bin/PSModuleDevelopment.xml | 24 ++ .../templating/Invoke-PSMDTemplate.ps1 | 30 ++- .../functions/templating/New-PSMDTemplate.ps1 | 6 +- .../template/Invoke-PsmdTemplateV1.ps1 | 243 ++++++++++++++++++ .../Condition/Condition.cs | 144 +++++++++++ .../Condition/ConditionContext.cs | 56 ++++ .../Condition/ConditionHost.cs | 123 +++++++++ .../PSModuleDevelopment/Condition/RuleAnd.cs | 99 +++++++ .../PSModuleDevelopment/Condition/RuleBase.cs | 64 +++++ .../Condition/RuleLogEntry.cs | 54 ++++ .../Condition/RuleParseResult.cs | 95 +++++++ .../Format/ViewDefinitionBase.cs | 5 + .../PSModuleDevelopment.csproj | 1 + .../Template/ConditionalValue.cs | 22 ++ .../Template/FileScriptScope.cs | 27 ++ .../Template/ParameterBase.cs | 37 +++ .../Template/ParameterConditional.cs | 25 ++ .../Template/ParameterFileScript.cs | 22 ++ .../Template/ParameterMultipleChoice.cs | 22 ++ .../Template/ParameterScript.cs | 13 +- .../Template/ParameterType.cs | 34 +++ .../Template/ParameterValue.cs | 16 ++ .../PSModuleDevelopment/Template/Store.cs | 4 - .../PSModuleDevelopment/Template/Template.cs | 16 +- .../Template/TemplateInfo.cs | 9 +- .../Template/TemplateItemBase.cs | 3 - .../Template/TemplateItemFile.cs | 3 - .../Template/TemplateItemFolder.cs | 3 - .../Template/TemplateItemTemplate.cs | 44 ++++ .../Template/TemplateType.cs | 8 +- 33 files changed, 1209 insertions(+), 45 deletions(-) create mode 100644 PSModuleDevelopment/internal/functions/template/Invoke-PsmdTemplateV1.ps1 create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/Condition.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionContext.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionHost.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleBase.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleLogEntry.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleParseResult.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/ConditionalValue.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/FileScriptScope.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterBase.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterConditional.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterFileScript.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterMultipleChoice.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterType.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterValue.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemTemplate.cs diff --git a/PSModuleDevelopment/PSModuleDevelopment.psd1 b/PSModuleDevelopment/PSModuleDevelopment.psd1 index 5d89baf..ab304b1 100644 --- a/PSModuleDevelopment/PSModuleDevelopment.psd1 +++ b/PSModuleDevelopment/PSModuleDevelopment.psd1 @@ -41,7 +41,7 @@ # Modules that must be imported into the global environment prior to importing # this module - RequiredModules = @(@{ ModuleName='PSFramework'; ModuleVersion= '0.9.16.44' }) + RequiredModules = @(@{ ModuleName='PSFramework'; ModuleVersion= '0.9.23.82' }) # Assemblies that must be loaded prior to importing this module RequiredAssemblies = @('bin\PSModuleDevelopment.dll') diff --git a/PSModuleDevelopment/bin/PSModuleDevelopment.dll b/PSModuleDevelopment/bin/PSModuleDevelopment.dll index e1e4a15db94aea330f71351adc46d7b977d276b9..203c5c801f2585d58101c77c4cf1e4a464c0e705 100644 GIT binary patch delta 7219 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 delta 7121 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 TEB|2VExjY|n)lVwF6;jQMT}(9 diff --git a/PSModuleDevelopment/bin/PSModuleDevelopment.pdb b/PSModuleDevelopment/bin/PSModuleDevelopment.pdb index ad21083590ec1f43252f7088c18eeaf72ed9dec9..54cf412c0c0704e661b57a2f9001685852d26ea3 100644 GIT binary patch delta 4420 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 The format string to produce for the format file. + + + + + + + Class containing information about a class's constructor @@ -383,6 +390,11 @@ List of parameters the template accepts + + + List of descriptions for parameters. Will be displayed if the corresponding parameter is being prompted for + + List of scripts that will be invoked on initialization @@ -393,6 +405,12 @@ Items in the root directory of the template (which may contain children themselves). + + + Version of the template schema used to create the template. + Used to track legacy templates and prevent breaking their invocation. + + Returns the template digest used as index file. @@ -454,6 +472,12 @@ The path to the template file + + + Version of the template schema used to create the template. + Used to track legacy templates and prevent breaking their invocation. + + The version-qualified name of the template diff --git a/PSModuleDevelopment/functions/templating/Invoke-PSMDTemplate.ps1 b/PSModuleDevelopment/functions/templating/Invoke-PSMDTemplate.ps1 index e5f81cb..cbb3144 100644 --- a/PSModuleDevelopment/functions/templating/Invoke-PSMDTemplate.ps1 +++ b/PSModuleDevelopment/functions/templating/Invoke-PSMDTemplate.ps1 @@ -416,7 +416,20 @@ { if ($PSCmdlet.ShouldProcess($item, "Invoking template")) { - try { Invoke-Template -Template $item -OutPath $resolvedPath.ProviderPath -NoFolder $NoFolder -Parameters $Parameters.Clone() -Raw $Raw -Silent $Silent } + try + { + switch ("$($item.SchemaVersion)") + { + "2.0" + { + + } + default + { + Invoke-PsmdTemplateV1 -TemplateInfo $item -OutPath $resolvedPath.ProviderPath -NoFolder $NoFolder -Parameters $Parameters.Clone() -Raw $Raw -Silent $Silent + } + } + } catch { Stop-PSFFunction -Message "Failed to invoke template $($item)" -EnableException $EnableException -ErrorRecord $_ -Target $item -Tag 'fail', 'template', 'invoke' -Continue } } } @@ -424,7 +437,20 @@ { if ($PSCmdlet.ShouldProcess($item, "Invoking template")) { - try { Invoke-Template -Template $item -OutPath $resolvedPath.ProviderPath -NoFolder $NoFolder -Parameters $Parameters.Clone() -Raw $Raw -Silent $Silent } + try + { + switch ("$($item.SchemaVersion)") + { + "2.0" + { + + } + default + { + Invoke-PsmdTemplateV1 -TemplateInfo $item -OutPath $resolvedPath.ProviderPath -NoFolder $NoFolder -Parameters $Parameters.Clone() -Raw $Raw -Silent $Silent + } + } + } catch { Stop-PSFFunction -Message "Failed to invoke template $($item)" -EnableException $EnableException -ErrorRecord $_ -Target $item -Tag 'fail', 'template', 'invoke' -Continue } } } diff --git a/PSModuleDevelopment/functions/templating/New-PSMDTemplate.ps1 b/PSModuleDevelopment/functions/templating/New-PSMDTemplate.ps1 index 55d5278..e66d2f7 100644 --- a/PSModuleDevelopment/functions/templating/New-PSMDTemplate.ps1 +++ b/PSModuleDevelopment/functions/templating/New-PSMDTemplate.ps1 @@ -197,7 +197,7 @@ if ($templateData.AutoIncrementVersion) { - $oldTemplate = Get-PSMDTemplate -TemplateName $template.Name -WarningAction SilentlyContinue | Sort-Object Version | Select-Object -First 1 + $oldTemplate = Get-PSMDTemplate -TemplateName $template.Name -WarningAction SilentlyContinue | Sort-Object Version -Descending | Select-Object -First 1 if (($oldTemplate) -and ($oldTemplate.Version -ge $template.Version)) { $major = $oldTemplate.Version.Major @@ -287,7 +287,6 @@ [System.IO.FileSystemInfo] $Item, - [PSModuleDevelopment.Template.TemplateItemBase] $Parent, [string] @@ -296,7 +295,6 @@ [string[]] $Exclusions, - [PSModuleDevelopment.Template.Template] $Template, [string] @@ -552,7 +550,7 @@ $template.CreatedOn = (Get-Date).Date - $template | Export-Clixml -Path (Join-Path $exportFolder $fileName) + [System.IO.File]::WriteAllText((Join-Path (Resolve-Path $exportFolder) $fileName), ([PSFramework.Utility.UtilityHost]::CompressString(([System.Management.Automation.PSSerializer]::Serialize($template))))) $template.ToTemplateInfo() | Export-Clixml -Path (Join-Path $exportFolder $infoFileName) } } \ No newline at end of file diff --git a/PSModuleDevelopment/internal/functions/template/Invoke-PsmdTemplateV1.ps1 b/PSModuleDevelopment/internal/functions/template/Invoke-PsmdTemplateV1.ps1 new file mode 100644 index 0000000..4b2d910 --- /dev/null +++ b/PSModuleDevelopment/internal/functions/template/Invoke-PsmdTemplateV1.ps1 @@ -0,0 +1,243 @@ +function Invoke-PsmdTemplateV1 +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] + [CmdletBinding()] + param ( + $TemplateInfo, + + [string] + $OutPath, + + [bool] + $NoFolder, + + [hashtable] + $Parameters, + + [bool] + $Raw, + + [bool] + $Silent + ) + Write-PSFMessage -Level Verbose -Message "Processing template $($item)" -Tag 'template', 'invoke' -FunctionName Invoke-PSMDTemplate + + #region Helper Function + function Write-TemplateItem + { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] + [CmdletBinding()] + param ( + [PSModuleDevelopment.Template.TemplateItemBase] + $Item, + + [string] + $Path, + + [hashtable] + $ParameterFlat, + + [hashtable] + $ParameterScript, + + [bool] + $Raw + ) + + Write-PSFMessage -Level Verbose -Message "Creating file: $($Item.Name) ($($Item.RelativePath))" -FunctionName Invoke-PSMDTemplate -ModuleName PSModuleDevelopment -Tag 'create', 'template' + + $identifier = $Item.Identifier + $isFile = $Item.GetType().Name -eq 'TemplateItemFile' + + #region File + if ($isFile) + { + $fileName = $Item.Name + if (-not $Raw) + { + foreach ($param in $Item.FileSystemParameterFlat) + { + $fileName = $fileName -replace "$($identifier)$([regex]::Escape($param))$($identifier)", $ParameterFlat[$param] + } + foreach ($param in $Item.FileSystemParameterScript) + { + $fileName = $fileName -replace "$($identifier)!$([regex]::Escape($param))!$($identifier)", $ParameterScript[$param] + } + } + $destPath = Join-Path $Path $fileName + + if ($Item.PlainText) + { + $text = $Item.Value + if (-not $Raw) + { + foreach ($param in $Item.ContentParameterFlat) + { + $text = $text -replace "$($identifier)$([regex]::Escape($param))$($identifier)", $ParameterFlat[$param] + } + foreach ($param in $Item.ContentParameterScript) + { + $text = $text -replace "$($identifier)!$([regex]::Escape($param))!$($identifier)", $ParameterScript[$param] + } + } + [System.IO.File]::WriteAllText($destPath, $text) + } + else + { + $bytes = [System.Convert]::FromBase64String($Item.Value) + [System.IO.File]::WriteAllBytes($destPath, $bytes) + } + } + #endregion File + + #region Folder + else + { + $folderName = $Item.Name + if (-not $Raw) + { + foreach ($param in $Item.FileSystemParameterFlat) + { + $folderName = $folderName -replace "$($identifier)$([regex]::Escape($param))$($identifier)", $ParameterFlat[$param] + } + foreach ($param in $Item.FileSystemParameterScript) + { + $folderName = $folderName -replace "$($identifier)!$([regex]::Escape($param))!$($identifier)", $ParameterScript[$param] + } + } + $folder = New-Item -Path $Path -Name $folderName -ItemType Directory + + foreach ($child in $Item.Children) + { + Write-TemplateItem -Item $child -Path $folder.FullName -ParameterFlat $ParameterFlat -ParameterScript $ParameterScript -Raw $Raw + } + } + #endregion Folder + } + #endregion Helper Function + + $templateData = Import-Clixml -Path $TemplateInfo.Path -ErrorAction Stop + #region Process Parameters + foreach ($parameter in $templateData.Parameters) + { + if (-not $parameter) { continue } + if (-not $Parameters.ContainsKey($parameter)) + { + if ($Silent) { throw "Parameter not specified: $parameter" } + try + { + $value = Read-Host -Prompt "Enter value for parameter '$parameter'" -ErrorAction Stop + $Parameters[$parameter] = $value + } + catch { throw } + } + } + #endregion Process Parameters + + #region Scripts + $scriptParameters = @{ } + + if (-not $Raw) + { + foreach ($scriptParam in $templateData.Scripts.Values) + { + if (-not $scriptParam) { continue } + try { $scriptParameters[$scriptParam.Name] = "$([scriptblock]::Create($scriptParam.StringScript).Invoke())" } + catch + { + if ($Silent) { throw (New-Object System.Exception("Scriptblock $($scriptParam.Name) failed during execution: $_", $_.Exception)) } + else + { + Write-PSFMessage -Level Warning -Message "Scriptblock $($scriptParam.Name) failed during execution. Please specify a custom value or use CTRL+C to terminate creation" -ErrorRecord $_ -FunctionName "Invoke-PSMDTemplate" -ModuleName 'PSModuleDevelopment' + $scriptParameters[$scriptParam.Name] = Read-Host -Prompt "Value for script $($scriptParam.Name)" + } + } + } + } + #endregion Scripts + + switch ($templateData.Type.ToString()) + { + #region File + "File" + { + foreach ($child in $templateData.Children) + { + Write-TemplateItem -Item $child -Path $OutPath -ParameterFlat $Parameters -ParameterScript $scriptParameters -Raw $Raw + } + if ($Raw -and $templateData.Scripts.Values) + { + $templateData.Scripts.Values | Export-Clixml -Path (Join-Path $OutPath "_PSMD_ParameterScripts.xml") + } + } + #endregion File + + #region Project + "Project" + { + #region Resolve output folder + if (-not $NoFolder) + { + if ($Parameters["Name"]) + { + $projectName = $Parameters["Name"] + $projectFullName = Join-Path $OutPath $projectName + if ((Test-Path $projectFullName) -and (-not $Force)) + { + throw "Project root folder already exists: $projectFullName" + } + $newFolder = New-Item -Path $OutPath -Name $Parameters["Name"] -ItemType Directory -ErrorAction Stop -Force + } + else + { + throw "Parameter Name is needed to create a project without setting the -NoFolder parameter!" + } + } + else { $newFolder = Get-Item $OutPath } + #endregion Resolve output folder + + foreach ($child in $templateData.Children) + { + Write-TemplateItem -Item $child -Path $newFolder.FullName -ParameterFlat $Parameters -ParameterScript $scriptParameters -Raw $Raw + } + + #region Write Config File (Raw) + if ($Raw) + { + $guid = [System.Guid]::NewGuid().ToString() + $optionsTemplate = @" +@{ + TemplateName = "$($TemplateInfo.Name)" + Version = ([Version]"$($TemplateInfo.Version)") + Tags = $(($TemplateInfo.Tags | ForEach-Object { "'$_'" }) -join ",") + Author = "$($TemplateInfo.Author)" + Description = "$($TemplateInfo.Description)" +þþþPLACEHOLDER-$($guid)þþþ +} +"@ + if ($params = $templateData.Scripts.Values) + { + $list = @() + foreach ($param in $params) + { + $list += @" + $($param.Name) = { + $($param.StringScript) + } +"@ + } + $optionsTemplate = $optionsTemplate -replace "þþþPLACEHOLDER-$($guid)þþþ", ($list -join "`n`n") + } + else + { + $optionsTemplate = $optionsTemplate -replace "þþþPLACEHOLDER-$($guid)þþþ", "" + } + + $configFile = Join-Path $newFolder.FullName "PSMDTemplate.ps1" + Set-Content -Path $configFile -Value $optionsTemplate -Encoding UTF8 + } + #endregion Write Config File (Raw) + } + #endregion Project + } +} \ No newline at end of file diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/Condition.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/Condition.cs new file mode 100644 index 0000000..74f0a1e --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/Condition.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; + +namespace PSModuleDevelopment.Condition +{ + /// + /// A condition is a context specific wrapper for a condition syntax, serving as the framework for dynamic filter/condition syntax that must be implemented in context-specific conditions. + /// + [Serializable] + public sealed class Condition + { + /// + /// Name of the context to apply. + /// A context is a set of conditions grouped to be legal in combination. + /// An application must first initialize a context before it can apply / use conditions within that same context. + /// + public string Context + { + get { return _Context.Name; } + set { _Context = ConditionHost.Contexts[value]; } + } + + /// + /// The actual context object defining the condition's rule + /// + private ConditionContext _Context; + + /// + /// The rule by which to comply. + /// Generally, the first rule is an AND or OR condition, but this is by no means mandatory. + /// + public RuleBase Rule + { + get { return _Rule; } + set + { + if (!IsValidRule(value)) + throw new ArgumentException("Rule is not a type of rule valid for this condition!"); + _Rule = value; + _Rule.Condition = this; + } + } + private RuleBase _Rule; + + /// + /// Validates a given item against the rules contained within. + /// + /// The item to validate + /// Whether the item can be validated against this rule. + public bool Validate(object Item) + { + if (Rule == null) + return false; + Log.Clear(); + return Rule.Validate(Item); + } + + /// + /// Whether a given rule is valid for this condition + /// + /// The rule to validate + /// Whether it is valid for this condition + public bool IsValidRule(RuleBase Rule) + { + // Universal rules - that is core logic rules, such as NOT, OR or AND - are valid for all conditions + if (Rule.IsUniversal) + return true; + + if (_Context.Rules.Contains(Rule.Id)) + return true; + return false; + } + + /// + /// The log containing the logging entries of the rules of this condition. + /// + public List Log = new List(); + + /// + /// Writes a log entry to the log. + /// + /// The name of the rule evaluated + /// The ID of the rule evaluated + /// Whether the result was positive + /// Any message to pass along + /// Any errors that occured + public void WriteLog(string RuleName, Guid RuleId, bool Success, string Message, Exception Error) + { + Log.Add(new RuleLogEntry(RuleName, RuleId, Success, Message, Error)); + } + + /// + /// Generates a fresh instance of the defined condition. + /// Discards logs, but copies all other relevnt content. + /// + /// A fresh Condition instance, ready to rock. + public Condition Clone() + { + Condition condition = new Condition(); + condition._Context = _Context; + condition._Rule = _Rule.Clone(); + return condition; + } + + /// + /// Returns the only rule object with the specified name that is legal for this condition. + /// Ambiguous names will cause an exception to be thrown. + /// + /// The name of the rule to generate. + /// A Rule object that owns that name. + public RuleBase GetRule(string Name) + { + RuleBase rule = _Context.GetRule(Name); + rule.Name = Name; + rule.Condition = this; + return rule; + } + + /// + /// Returns the only legal rule based on the parsed name of the condition string. + /// Preinitializes that rule object with its parsed filter string and assigns it to this condition object. + /// + /// The result of parsing a filter string into rule and rule-filter + /// A finished rule object ready to use + public RuleBase GetRule(RuleParseResult ParseResult) + { + RuleBase rule = GetRule(ParseResult.Name); + rule.Parse(ParseResult.Condition); + return rule; + } + + /// + /// Load a condition string and generate rules from it. + /// + /// The condition string to break down into the set of rules to validate. + public void Load(string Condition) + { + RuleParseResult parsed = new RuleParseResult(Condition); + RuleBase ruleItem = GetRule(parsed.Name); + ruleItem.Parse(parsed.Condition); + Rule = ruleItem; + } + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionContext.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionContext.cs new file mode 100644 index 0000000..123bc60 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionContext.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +namespace PSModuleDevelopment.Condition +{ + /// + /// A condition context defines, what conditions may be part of a given condition type (="Context") + /// + [Serializable] + public sealed class ConditionContext + { + /// + /// Name of the context as seen by the world + /// + public string Name; + + /// + /// List of all rules that are part of this context. + /// + public List Rules = new List(); + + /// + /// Resolves the name of a rule to the actual rule object legal for this context. + /// Multiple rules may share a name at any given time, but in order for a name to be usable within a context, it must be unqiue within the rules supported by that context. + /// + /// The name to search by + /// The rule that applies to this context. + public RuleBase GetRule(string Name) + { + List rules = ConditionHost.GetRules(Name); + List rulesLegal = new List(); + foreach (RuleBase rule in rules) + if (Rules.Contains(rule.Id)) + rulesLegal.Add(rule); + + if (rulesLegal.Count > 1) + throw new ArgumentException(String.Format("Ambiguous rule resolution! Found {0} rules using the name {1} in the context {2}", rulesLegal.Count, Name, this.Name)); + + if (rulesLegal.Count < 1) + throw new ArgumentException(String.Format("Found no rules using the name {0} in the context {1}", Name, this.Name)); + return rules[0]; + } + + /// + /// Return all rules that are part of this context + /// + /// List of all rules that are part of this context. + public List GetRules() + { + List rules = new List(); + foreach (Guid id in Rules) + rules.Add(ConditionHost.GetRule(id)); + return rules; + } + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionHost.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionHost.cs new file mode 100644 index 0000000..84d681e --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/ConditionHost.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PSModuleDevelopment.Condition +{ + /// + /// Class hosting static tools used when working with the condition system. + /// + public static class ConditionHost + { + /// + /// Table of registered contexts + /// + public static Dictionary Contexts = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + /// + /// Table of registered Rules + /// + public static Dictionary Rules = new Dictionary(); + + /// + /// Returns a specific rule based on its Guid + /// + /// The unique ID of the rule to return + /// The rule matching the Guid + public static RuleBase GetRule(Guid Id) + { + if (!Rules.ContainsKey(Id)) + throw new KeyNotFoundException(String.Format("A rule with Id '{0}' has not been registered yet!", Id)); + return Rules[Id].Clone(); + } + + /// + /// Returns all rules with a matching name + /// + /// The name to look by. Does NOT support wildcards + /// All rules that use a given name + public static List GetRules(string Name) + { + List rules = new List(); + List ruleIDs = new List(); + + foreach (RuleBase rule in Rules.Values) + foreach (string name in rule.Names) + if (String.Equals(name, Name, StringComparison.InvariantCultureIgnoreCase) && !ruleIDs.Contains(rule.Id)) + { + rules.Add(rule.Clone()); + ruleIDs.Add(rule.Id); + } + + return rules; + } + + /// + /// Parses the rule from a filter string. + /// This parser assumes the name to be delimited from anything coming after it by either a whitespace or an open parens. + /// The condition will be trimmed, so working with leading or trailing whitespace is impossible, but additional ones will not hurt. + /// + /// The filter to consider + /// The parsed name and condition + public static RuleParseResult ParseRule(string Filter) + { + return new RuleParseResult(Filter); + } + + /// + /// Splits a string containing one or multiple filter rules. + /// Used by rules that handle multiple conditions, such as AND or OR rules. + /// + /// The string to separate out. + /// A list of strings that each represent a rule. + public static List SplitRulesString(string Filter) + { + List result = new List(); + + int braceLevel = 0; + int position = 0; + string tempFilter = Filter.Trim(); + char[] content = tempFilter.ToCharArray(); + bool inCondition = false; + StringBuilder builder = new StringBuilder(); + + while (position < content.Length) + { + char curItem = content[position]; + switch (curItem) + { + case '(': + braceLevel++; + builder.Append(curItem); + inCondition = true; + break; + case ')': + braceLevel--; + builder.Append(curItem); + if (braceLevel == 0) + { + inCondition = false; + result.Add(builder.ToString()); + builder = new StringBuilder(); + } + if (braceLevel < 0) + throw new ArgumentException(String.Format("Failed to parse '{0}' at position {1}", tempFilter, position)); + break; + case ' ': + if (inCondition) + builder.Append(curItem); + break; + default: + if (!inCondition) + throw new ArgumentException(String.Format("Failed to parse '{0}' at position {1}", tempFilter, position)); + builder.Append(curItem); + break; + } + + position++; + } + + return result; + } + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs new file mode 100644 index 0000000..deaacba --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PSModuleDevelopment.Condition +{ + /// + /// The rule implementing a logical AND condition + /// + public class RuleAnd : RuleBase + { + /// + /// The ID of the rule + /// + public override Guid Id + { + get { return new Guid("6d4a7080-a69e-4ea1-a9ed-bfa5da729bba"); } + } + + /// + /// The names you can call the rule by + /// + public override string[] Names + { + get + { + return new string[] { "&", "and" }; + } + } + + /// + /// The condition governing this rule + /// + public new Condition Condition + { + get + { + return _Condition; + } + set + { + _Condition = value; + foreach (RuleBase rule in Rules) + rule.Condition = value; + } + } + private Condition _Condition; + + /// + /// Contains all rules that are chained in an AND chain + /// + public List Rules = new List(); + + /// + /// Create a true copy of the current item + /// + /// The cloned rule + public override RuleBase Clone() + { + RuleAnd rule = new RuleAnd(); + rule.Name = Name; + foreach (RuleBase ruleItem in Rules) + rule.Rules.Add(ruleItem.Clone()); + + return rule; + } + + /// + /// The string representation of this rule + /// + /// The string representation of this rule + public override string ToString() + { + return String.Format("({0}{1})", Name, String.Join("", Rules)); + } + + /// + /// Parses a filterstring into a list of rules, all of which must be true for this rule to be true. + /// + /// The string to interpret + public override void Parse(string String) + { + foreach (string ruleString in ConditionHost.SplitRulesString(String)) + Rules.Add(Condition.GetRule(new RuleParseResult(ruleString))); + } + + /// + /// Validates an input object by applying all rules contained in this rule, ALL of which must be true + /// + /// An arbitrary object to evaluate + /// Whetehr all conditions returned true or not. + public override bool Validate(object Item) + { + throw new NotImplementedException(); + } + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleBase.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleBase.cs new file mode 100644 index 0000000..c1ea4a4 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleBase.cs @@ -0,0 +1,64 @@ +using System; + +namespace PSModuleDevelopment.Condition +{ + /// + /// The base class rules are built upon. + /// + public abstract class RuleBase + { + /// + /// The condition the rule is a part of + /// + public Condition Condition { get; set; } + + /// + /// The unique id each rule must have. + /// + public abstract Guid Id { get; } + + /// + /// The name of the rule + /// + public abstract string[] Names { get; } + + /// + /// The name the rule was actually selected by. + /// + public string Name { get; set; } + + /// + /// Whether a rule is universal. Common language rules are universal + /// + internal bool IsUniversal + { + get { return false; } + } + + /// + /// Parse an input-string to produce the actual condition rule(s) represented by it. + /// + /// The string to parse into rules + public abstract void Parse(string String); + + /// + /// Creates a copy of itself + /// + /// A copy of itself + public abstract RuleBase Clone(); + + /// + /// Validates the input item against the rule contained + /// + /// The object to validate + /// The condition the rule is part of + /// Whether the item meets the implemented rule's requirements + public abstract bool Validate(object Item); + + /// + /// Method that overrides the default string output. + /// + /// The filter string which - when passed to parse - will generate this object + public abstract override string ToString(); + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleLogEntry.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleLogEntry.cs new file mode 100644 index 0000000..94bc85f --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleLogEntry.cs @@ -0,0 +1,54 @@ +using System; + +namespace PSModuleDevelopment.Condition +{ + /// + /// Contains information on a rules evaluation. + /// This is used to offer diagnostics to the system using this condition component. + /// It is not meant as a means for conditions to return their validation output (which is done using the Validate method). + /// + public class RuleLogEntry + { + /// + /// THe name of the rule that was evaluated + /// + public string RuleName; + + /// + /// The guid of the rule that was evaluated + /// + public Guid RuleId; + + /// + /// Whether evaluation was a success + /// + public bool Success; + + /// + /// Any message to pass along to the system + /// + public string Message; + + /// + /// If an actual exception occured, pass that along also + /// + public Exception Error; + + /// + /// Creates a new log entry, containing the result of a rule's evaluation. + /// + /// The name of the rule that was evaluated + /// The ID of the rule that was evaluated. + /// Whether the rule evaluated the target as valid + /// Any message to pass to the system + /// Any error that occured. + public RuleLogEntry(string RuleName, Guid RuleId, bool Success, string Message, Exception Error) + { + this.RuleName = RuleName; + this.RuleId = RuleId; + this.Success = Success; + this.Message = Message; + this.Error = Error; + } + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleParseResult.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleParseResult.cs new file mode 100644 index 0000000..18ad3d1 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleParseResult.cs @@ -0,0 +1,95 @@ +using System; +using System.Text; + +namespace PSModuleDevelopment.Condition +{ + /// + /// Conveys the result of a rule parsing + /// + public class RuleParseResult + { + /// + /// Name of the rule + /// + public string Name; + + /// + /// COndition text + /// + public string Condition; + + /// + /// Constructs a parse result from an input string to parse. + /// + /// The string to dissect into a rule + public RuleParseResult(string Filter) + { + if (Filter.Length == 0) + throw new ArgumentNullException("Can't process an empty string"); + char[] characters = Filter.ToCharArray(); + if (characters[0] != '(') + throw new ArgumentException("A rule section must start with a '(' character!"); + + #region Parsing the name + StringBuilder stringBuilder = new StringBuilder(); + int position = 1; + bool killIt = false; + while (!killIt) + { + if (characters.Length <= position) + break; + switch (characters[position]) + { + case ' ': + killIt = true; + break; + case '\t': + killIt = true; + break; + case '(': + killIt = true; + break; + case ')': + killIt = true; + break; + default: + stringBuilder.Append(characters[position]); + break; + } + + position++; + } + + Name = stringBuilder.ToString(); + if (String.IsNullOrEmpty(Name)) + throw new ArgumentException(String.Format("Could not resolve a legal name from this condition string: {0}", Filter)); + #endregion Parsing the name + + #region Parsing the filter condition + stringBuilder = new StringBuilder(); + int braceLevel = 1; + while (position < Filter.Length) + { + stringBuilder.Append(characters[position]); + + if (characters[position] == '(') + braceLevel++; + if (characters[position] == ')') + braceLevel--; + + position++; + + if (braceLevel < 0) + throw new ArgumentException(String.Format("Failed to parse content of {0} rule!", Name)); + if ((braceLevel == 0) && (position < Filter.Length)) + throw new ArgumentException(String.Format("Failed to parse content of {0} rule!", Name)); + } + + if (braceLevel > 0) + throw new ArgumentException(String.Format("Failed to parse content of {0} rule!", Name)); + + Condition = stringBuilder.ToString().Trim(); + #endregion Parsing the filter condition + } + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Format/ViewDefinitionBase.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Format/ViewDefinitionBase.cs index 081b05a..fc828aa 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Format/ViewDefinitionBase.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Format/ViewDefinitionBase.cs @@ -37,6 +37,11 @@ public abstract class ViewDefinitionBase : IComparable /// The format string to produce for the format file. public abstract override string ToString(); + /// + /// + /// + /// + /// public int CompareTo(object Item) { ViewDefinitionBase tempItem = Item as ViewDefinitionBase; diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj b/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj index 3471656..d960704 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj +++ b/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj @@ -61,6 +61,7 @@ + diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ConditionalValue.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ConditionalValue.cs new file mode 100644 index 0000000..e939d6a --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ConditionalValue.cs @@ -0,0 +1,22 @@ +using PSModuleDevelopment.Condition; +using System; + +namespace PSModuleDevelopment.Template +{ + /// + /// A value tied to a set of conditions, ALL of which must be true in order for the valuer ot apply + /// + [Serializable] + public class ConditionalValue + { + /// + /// The value to apply if conditions are met + /// + public string Value; + + /// + /// + /// + public Condition.Condition Condition; + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/FileScriptScope.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/FileScriptScope.cs new file mode 100644 index 0000000..4628894 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/FileScriptScope.cs @@ -0,0 +1,27 @@ +using System; + +namespace PSModuleDevelopment.Template +{ + /// + /// WHen a file script parameter will be executed + /// + [Flags] + public enum FileScriptScope + { + /// + /// Execute after name has been determined but before any other processing has been performed. + /// + Begin, + + /// + /// Execute during file creation. Executed after file content has been determined but before it is written. + /// Able to affect file content to be written. + /// + Process, + + /// + /// Execute after the file content has been written. + /// + End + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterBase.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterBase.cs new file mode 100644 index 0000000..8200660 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterBase.cs @@ -0,0 +1,37 @@ +using System; + +namespace PSModuleDevelopment.Template +{ + /// + /// Base class for all parameters + /// + [Serializable] + public abstract class ParameterBase + { + /// + /// Name of the parameter + /// + public string Name; + + /// + /// A description for the parameter. + /// In case of parameter types interacting with the user, this may also be used to explain the parameter to the user. + /// + public string Description; + + /// + /// The value that was assigned to the parameter. Filled by logic executed or values chosen. + /// + public string Value; + + /// + /// The type of parameter this is + /// + public ParameterType Type; + + /// + /// A unique ID for a given parameter. Names should be unique, but this allows for future need. + /// + public Guid Id; + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterConditional.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterConditional.cs new file mode 100644 index 0000000..51bacb2 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterConditional.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PSModuleDevelopment.Template +{ + /// + /// A set of values that depend on a given condition. + /// + [Serializable] + public class ParameterConditional : ParameterBase + { + /// + /// If no condition matches, this value is picked. + /// + public string DefaultValue; + + /// + /// The first condition to match will be applied + /// + public List Values = new List(); + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterFileScript.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterFileScript.cs new file mode 100644 index 0000000..0bda5f3 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterFileScript.cs @@ -0,0 +1,22 @@ +using System; +using System.Management.Automation; + +namespace PSModuleDevelopment.Template +{ + /// + /// A script executed in the context of a template file it is attached to. + /// + [Serializable] + public class ParameterFileScript : ParameterBase + { + /// + /// The scriptblock to execute + /// + public ScriptBlock ScriptBlock; + + /// + /// The scope during which the sciprtblock will be executed + /// + public FileScriptScope Scope = FileScriptScope.End; + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterMultipleChoice.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterMultipleChoice.cs new file mode 100644 index 0000000..c4e818c --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterMultipleChoice.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; + +namespace PSModuleDevelopment.Template +{ + /// + /// Parameter representing a multiple-choice option + /// + [Serializable] + public class ParameterMultipleChoice : ParameterBase + { + /// + /// The options available to the user + /// + public List Options = new List(); + + /// + /// Descriptions for the choices offered to the user + /// + public Dictionary Descriptions = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterScript.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterScript.cs index 1e0b020..72a0ce4 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterScript.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterScript.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Management.Automation; -using System.Text; -using System.Threading.Tasks; namespace PSModuleDevelopment.Template { @@ -11,13 +7,8 @@ namespace PSModuleDevelopment.Template /// A script used to calculate content to be inserted /// [Serializable] - public class ParameterScript + public class ParameterScript : ParameterBase { - /// - /// Name of the scriptblock (auto-assigned or specified) - /// - public string Name; - /// /// The scriptblock to invoke /// @@ -44,7 +35,7 @@ public ParameterScript() /// Creates a prefilled paramter script. Usually used when creating templates /// /// The name of the parameter. This is used to insert it into individual files or file names. - /// + /// The scriptblock to execute public ParameterScript(string Name, ScriptBlock ScriptBlock) { this.Name = Name; diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterType.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterType.cs new file mode 100644 index 0000000..349ada7 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterType.cs @@ -0,0 +1,34 @@ +namespace PSModuleDevelopment.Template +{ + /// + /// The type of parameter a parameter is + /// + public enum ParameterType + { + /// + /// A plain value parameter - the user may be prompted to fill this in + /// + Value, + + /// + /// A script parameter - this logic will be executed to produce text to be inserted + /// + Script, + + /// + /// A script that will be executed in the context of the file it was assigned to. + /// It can be assigned to run before or after creating the file. + /// + FileScript, + + /// + /// Prompting the user for multiple choice + /// + MultipleChoice, + + /// + /// A value that depends on a given condition + /// + ConditionalValue, + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterValue.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterValue.cs new file mode 100644 index 0000000..db7f408 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/ParameterValue.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PSModuleDevelopment.Template +{ + /// + /// A common value parameter. My be specified or prompted for during invocation + /// + [Serializable] + public class ParameterValue : ParameterBase + { + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/Store.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/Store.cs index a07049f..6313c31 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/Store.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/Store.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace PSModuleDevelopment.Template { diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/Template.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/Template.cs index 77b7ff8..4639326 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/Template.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/Template.cs @@ -1,9 +1,5 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace PSModuleDevelopment.Template { @@ -53,6 +49,11 @@ public class Template /// public List Parameters = new List(); + /// + /// List of descriptions for parameters. Will be displayed if the corresponding parameter is being prompted for + /// + public Dictionary ParameterDescriptions = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + /// /// List of scripts that will be invoked on initialization /// @@ -63,6 +64,12 @@ public class Template /// public List Children = new List(); + /// + /// Version of the template schema used to create the template. + /// Used to track legacy templates and prevent breaking their invocation. + /// + public Version SchemaVersion = new Version(2, 0); + /// /// Returns the template digest used as index file. /// @@ -78,6 +85,7 @@ public TemplateInfo ToTemplateInfo() info.Tags = Tags; info.Type = Type; info.Version = Version; + info.SchemaVersion = SchemaVersion; return info; } diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateInfo.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateInfo.cs index 586bc40..e64c17e 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateInfo.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateInfo.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace PSModuleDevelopment.Template { @@ -62,6 +59,12 @@ public class TemplateInfo /// public string Path; + /// + /// Version of the template schema used to create the template. + /// Used to track legacy templates and prevent breaking their invocation. + /// + public Version SchemaVersion; + /// /// The version-qualified name of the template /// diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemBase.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemBase.cs index fb10d61..3eb2370 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemBase.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemBase.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace PSModuleDevelopment.Template { diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFile.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFile.cs index e936a5b..51552ec 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFile.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFile.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace PSModuleDevelopment.Template { diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFolder.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFolder.cs index f10bda6..5649d70 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFolder.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemFolder.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace PSModuleDevelopment.Template { diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemTemplate.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemTemplate.cs new file mode 100644 index 0000000..6a60280 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateItemTemplate.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PSModuleDevelopment.Template +{ + /// + /// Class implementing a template referencing another template + /// + public class TemplateItemTemplate : TemplateItemBase + { + /// + /// Name of the template to initialize + /// + public string TemplateName; + + /// + /// The minimum version required for the template to invoke. + /// + public Version MinimumVersion; + + /// + /// Exactly this version and only this version may be used + /// + public Version RequiredVersion; + + /// + /// Which parameters should be inherited from the parent template + /// + public List ParametersToInherit; + + /// + /// How many times should the template be invoked + /// + public int Count = 1; + + /// + /// Whether the template should be looped over repeatedly, offering the user to create any number of instances of the template. + /// + public bool Loop = false; + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateType.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateType.cs index 38c4e50..08375c6 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateType.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Template/TemplateType.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PSModuleDevelopment.Template +namespace PSModuleDevelopment.Template { /// /// Is the template a single file or a project? From 51ed5a7641d5349413829f07ec8324e5c444b6a6 Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Wed, 8 Aug 2018 23:11:11 +0200 Subject: [PATCH 2/4] missing project file update --- .../PSModuleDevelopment.csproj | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj b/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj index d960704..3d9bede 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj +++ b/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj @@ -46,6 +46,13 @@ + + + + + + + @@ -54,7 +61,15 @@ + + + + + + + + From 8462566126820e74b0c209598872cb3c104bf52c Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Wed, 8 Aug 2018 23:11:23 +0200 Subject: [PATCH 3/4] Implemented validation --- .../PSModuleDevelopment/Condition/RuleAnd.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs index deaacba..160f3f4 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RuleAnd.cs @@ -93,7 +93,16 @@ public override void Parse(string String) /// Whetehr all conditions returned true or not. public override bool Validate(object Item) { - throw new NotImplementedException(); + foreach (RuleBase rule in Rules) + { + if (!rule.Validate(Item)) + { + Condition.WriteLog(Name, Id, false, "", null); + return false; + } + } + Condition.WriteLog(Name, Id, true, "", null); + return true; } } } From 3e865e3a298d71bdbfa78abfa475e072015184a5 Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Sun, 26 Aug 2018 09:21:02 +0200 Subject: [PATCH 4/4] more changes --- PSModuleDevelopment/PSModuleDevelopment.psd1 | 4 +- .../Condition/PSCompareOperator.cs | 43 ++++ .../Condition/RulePsobjectProperty.cs | 198 ++++++++++++++++++ .../PSModuleDevelopment.csproj | 2 + 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/PSCompareOperator.cs create mode 100644 library/PSModuleDevelopment/PSModuleDevelopment/Condition/RulePsobjectProperty.cs diff --git a/PSModuleDevelopment/PSModuleDevelopment.psd1 b/PSModuleDevelopment/PSModuleDevelopment.psd1 index ab304b1..ff41ad3 100644 --- a/PSModuleDevelopment/PSModuleDevelopment.psd1 +++ b/PSModuleDevelopment/PSModuleDevelopment.psd1 @@ -41,7 +41,9 @@ # Modules that must be imported into the global environment prior to importing # this module - RequiredModules = @(@{ ModuleName='PSFramework'; ModuleVersion= '0.9.23.82' }) + RequiredModules = @( + @{ ModuleName = 'PSFramework'; ModuleVersion = '0.9.23.82' } + ) # Assemblies that must be loaded prior to importing this module RequiredAssemblies = @('bin\PSModuleDevelopment.dll') diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/PSCompareOperator.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/PSCompareOperator.cs new file mode 100644 index 0000000..3dbf618 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/PSCompareOperator.cs @@ -0,0 +1,43 @@ +namespace PSModuleDevelopment.Condition +{ + /// + /// The powershell operator implemented in the + /// + public enum PSCompareOperator + { + /// + /// Don't do anything + /// + None, + + /// + /// Equality + /// + Equals, + + /// + /// Greater than + /// + GreaterThan, + + /// + /// Less than + /// + LessThan, + + /// + /// Similarity + /// + Like, + + /// + /// Regex Match + /// + Match, + + /// + /// Compare with null + /// + Null, + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RulePsobjectProperty.cs b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RulePsobjectProperty.cs new file mode 100644 index 0000000..f8d41a2 --- /dev/null +++ b/library/PSModuleDevelopment/PSModuleDevelopment/Condition/RulePsobjectProperty.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Text; +using System.Threading.Tasks; + +namespace PSModuleDevelopment.Condition +{ + /// + /// Rule that compares a property with a value + /// + public class RulePsobjectProperty : RuleBase + { + /// + public override Guid Id + { + get { return new Guid("9f454bda-b3e7-480d-8653-64ec714c1dd6"); } + } + + /// + public override string[] Names + { + get { return new string[] { "property", "psproperty", "prop" }; } + } + + /// + /// The property on the object to compare + /// + public string PropertyName; + + /// + /// The compare operator to use + /// + public PSCompareOperator Operator = PSCompareOperator.None; + + /// + /// The value to compare the property of the input with + /// + public string Value + { + get { return _Value; } + set + { + try + { + Number = Double.Parse(value); + IsNumber = true; + } + catch { } + if (value.ToLower() == "true") + { + BoolValue = true; + IsBool = true; + } + if (value.ToLower() == "false") + { + BoolValue = false; + IsBool = true; + } + _Value = value; + } + } + private string _Value; + + /// + /// The numeric value of the property + /// + public double Number; + + /// + /// Whether the value to compare with actually is numeric + /// + public bool IsNumber; + + /// + /// The boolean value of the object to compare with + /// + public bool BoolValue; + + /// + /// Whether the value to compare with actually is a boolean + /// + public bool IsBool; + + /// + public override RuleBase Clone() + { + RulePsobjectProperty rule = new RulePsobjectProperty(); + rule.Condition = Condition; + rule.Name = Name; + rule.PropertyName = PropertyName; + rule.Operator = Operator; + rule.Value = Value; + return rule; + } + + /// + public override string ToString() + { + return String.Format("({0} {1} {2} {3})", Name, PropertyName, GetOperator(Operator), Value); + } + + /// + public override void Parse(string String) + { + string[] values = String.Split(' '); + if (values.Length != 3) + throw new ArgumentException(String.Format("Invalid {0} filter! '{1}' must consist of three elements", Name, String)); + PropertyName = values[0]; + Operator = GetOperator(values[1]); + Value = values[2]; + } + + /// + public override bool Validate(object Item) + { + if (Item == null) + { + Condition.WriteLog(Name, Id, false, "Input object is null", null); + return false; + } + + PSObject item = Item as PSObject; + bool testResult = false; + + foreach (PSPropertyInfo property in item.Properties) + { + if (property.Name.ToLower() != PropertyName.ToLower()) + continue; + + if (Operator == PSCompareOperator.Null) + { + testResult = property.Value == null; + Condition.WriteLog(Name, Id, testResult, "", null); + return testResult; + } + + + } + + Condition.WriteLog(Name, Id, false, "Property not found", null); + return false; + } + + /// + /// Resolves an operator to its string form + /// + /// The operator to resolve + /// Its string representation + private string GetOperator(PSCompareOperator Operator) + { + switch (Operator) + { + case PSCompareOperator.Equals: + return "-eq"; + case PSCompareOperator.GreaterThan: + return "-gt"; + case PSCompareOperator.LessThan: + return "-lt"; + case PSCompareOperator.Like: + return "-like"; + case PSCompareOperator.Match: + return "-match"; + case PSCompareOperator.Null: + return "-null"; + default: + throw new ArgumentException("Unknown Operator!"); + } + } + + /// + /// Resolves an operator to its PSComperOperator form + /// + /// The string form of the operator + /// The resolved enum form of the operator + private PSCompareOperator GetOperator(string Operator) + { + switch (Operator.ToLower()) + { + case "-eq": + return PSCompareOperator.Equals; + case "-gt": + return PSCompareOperator.GreaterThan; + case "-lt": + return PSCompareOperator.LessThan; + case "-like": + return PSCompareOperator.Like; + case "-match": + return PSCompareOperator.Match; + case "-null": + return PSCompareOperator.Null; + default: + throw new ArgumentException("Unknown Operator!"); + } + } + } +} diff --git a/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj b/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj index 3d9bede..eda323d 100644 --- a/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj +++ b/library/PSModuleDevelopment/PSModuleDevelopment/PSModuleDevelopment.csproj @@ -49,10 +49,12 @@ + +