PlasterManifestDSL.psm1

Function Export-PlasterManifest
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Manifest,

        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            Mandatory = $true,
            Position = 1
        )]
        [string]
        $Destination,

        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            Position = 2
        )]
        [string]
        $Endcoding = 'default',

        [Parameter(
            ValueFromPipelineByPropertyName = $true
        )]
        [switch]
        $PassThru
    )

    #[xml]$xmlFile = $Manifest
    Out-File -FilePath $Destination -InputObject $Manifest -Encoding $Endcoding
    #$xmlFile.Save($Destination)

    if($PassThru)
    {
        Get-Item -Path $Destination
    }

}
Function New-PlasterContentBlock
{
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock
    )

    $subContent = $ScriptBlock.Invoke()
    $contentBlock = @"
<content>
$subContent
</content>
"@

    Write-Output -InputObject $contentBlock
}

New-Alias -Name Content -Value New-PlasterContentBlock
Function New-PlasterContentFile
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [AllowEmptyString()]
        [string]
        $Source,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 1
        )]
        [string]
        $Destination,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [scriptblock]
        $Condition,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [switch]
        $OpenInEditor,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Default','Ascii','BigEndianUnicode','BigEndianUTF32','Oem','Unicode','UTF32','UTF7','UTF8','UTF8-NoBOM')]
        [string]
        $Endcoding
    )

    New-PlasterContentBaseFile -FileType 'file' @PSBoundParameters
}

New-Alias -Name File -Value New-PlasterContentFile
Function New-PlasterContentMessage
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Message,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [scriptblock]
        $Condition,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [switch]
        $NoNewLine
    )

    $messageSB = [System.Text.StringBuilder]::new("<message")
    if ($NoNewLine)
    {
        $messageSB.Append(" nonewline='true'") > $null
    }
    
    if($Condition)
    {
        $messageSB.Append((New-Condition $Condition)) > $null
    }

    $messageSB.AppendLine(">$($Message)</message>") > $null
    Write-Output $messageSB.ToString()
}

New-Alias -Name Message -Value New-PlasterContentMessage  
Function New-PlasterContentNewModuleManifest
{
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock
    )

    $newScript = "[ordered]@{$($ScriptBlock.ToString())}"
    $newScriptBlock = [scriptblock]::Create($newScript)
    $manifestHash = & $newScriptBlock
    New-PlasterContentNewModuleManifestXML @manifestHash
}

New-Alias -Name NewModuleManifest -Value New-PlasterContentNewModuleManifest
Function New-PlasterContentRequireModule
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $MinimumVersion,

        [Parameter(
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $MaximumVersion,

        [Parameter(
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $RequiredVersion,

        [Parameter(
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Message,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [scriptblock]
        $Condition
    )

    $requiredModuleSB = [System.Text.StringBuilder]::new('<requireModule name="{0}"' -f $Name)

    if($MinimumVersion)
    {
        $requiredModuleSB.AppendLine(' minimumVersion="{0}"' -f $MinimumVersion) > $null
    }

    if($MaximumVersion)
    {
        $requiredModuleSB.AppendLine(' maximumVersion="{0}"' -f $MaximumVersion) > $null
    }

    if($RequiredVersion)
    {
        $requiredModuleSB.AppendLine(' requiredVersion="{0}"' -f $RequiredVersion) > $null
    }

    if($Message)
    {
        $requiredModuleSB.AppendLine(' message="{0}"' -f $Message) > $null
    }

    if($Condition)
    {
        $conditionString = New-Condition -Condition $Condition
        $requiredModuleSB.AppendLine(" $conditionString") > $null
    }

    $requiredModuleSB.AppendLine('/>') > $null
    Write-Output $requiredModuleSB.ToString()

}

New-Alias -Name RequireModule -Value New-PlasterContentRequireModule
Function New-PlasterContentTemplateFile
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Source,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 1
        )]
        [string]
        $Destination,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [scriptblock]
        $Condition,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [switch]
        $OpenInEditor,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Default','Ascii','BigEndianUnicode','BigEndianUTF32','Oem','Unicode','UTF32','UTF7','UTF8','UTF8-NoBOM')]
        [string]
        $Endcoding
    )

    New-PlasterContentBaseFile -FileType 'templateFile' @PSBoundParameters

}

New-Alias -Name TemplateFile -Value New-PlasterContentTemplateFile
Function New-PlasterManifestXML
{
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock
    )

    $manifestBody = $ScriptBlock.Invoke()
    $manifest = @"
<?xml version="1.0" encoding="UTF-8"?>
<plasterManifest xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1" schemaVersion="1.0">
$($manifestBody)
</plasterManifest>
"@

    Write-Output -InputObject $manifest
}

New-Alias -Name PlasterManifest -Value New-PlasterManifestXML
Function New-PlasterMetadataBlock
{
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock
    )

    $newScript = "[ordered]@{$($ScriptBlock.ToString())}"
    $newScriptBlock = [scriptblock]::Create($newScript)
    $metaHash = & $newScriptBlock
    New-MetadataXML @metaHash
}

New-Alias -Name Metadata -Value New-PlasterMetadataBlock
Function New-PlasterParameterBlock
{
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock
    )

    $subParams = $ScriptBlock.Invoke()
    $paramaterBlock = @"
<parameters>
$subParams
</parameters>
 
"@

    Write-Output -InputObject $paramaterBlock
}

New-Alias -Name Parameters -Value New-PlasterParameterBlock
Function New-PlasterParameterChoice
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Label,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Value,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Help
    )

    if(-not($Value))
    {
        $Value = $Label
    }

    $paramSB = [System.Text.StringBuilder]::new()
    $paramSB.AppendLine(" <choice label='&amp;$($Label)'") > $null
    $paramSB.AppendLine(" value='$($Value)'") > $null
    if ($Help)
    {
        $paramSB.AppendLine(" help='$($Help)'") > $null
    }

    $paramSB.Append('/>') > $null
    Write-Output $paramSB.ToString()
}

New-Alias -Name Choice -Value New-PlasterParameterChoice  
Function New-PlasterParameterMultiChoice
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Prompt,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Default,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Text','Encrypted')]
        [string]
        $Store,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock
    )

    New-PlasterParameterBaseChoice -ChoiceType "multichoice" @PSBoundParameters
}

New-Alias -Name MultiChoice -Value New-PlasterParameterMultiChoice
Function New-PlasterParameterSingleChoice
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Prompt,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Default,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Text','Encrypted')]
        [string]
        $Store,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock
    )

    New-PlasterParameterBaseChoice -ChoiceType "choice" @PSBoundParameters
}

New-Alias -Name SingleChoice -Value New-PlasterParameterSingleChoice  
Function New-PlasterParameterText
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Prompt,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Default,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Text','Encrypted')]
        [string]
        $Store
    )

    New-PlasterParameterBaseText -TextType 'text' @PSBoundParameters
}

New-Alias -Name Text -Value New-PlasterParameterText
Function New-PlasterParameterUserEmail
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Prompt,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Default, 

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Text','Encrypted')]
        [string]
        $Store
    )

    New-PlasterParameterBaseText -TextType 'user-email' @PSBoundParameters
}

New-Alias -Name UserEmail -Value New-PlasterParameterUserEmail
Function New-PlasterParameterUserFullName
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Prompt,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Default, 

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Text','Encrypted')]
        [string]
        $Store
    )

    New-PlasterParameterBaseText -TextType 'user-fullname' @PSBoundParameters
}

New-Alias -Name UserFullName -Value New-PlasterParameterUserFullName
Function New-Condition
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [scriptblock]
        $Condition
    )

    $conditionString = $Condition.Ast.EndBlock.Extent.Text
    $conditionString = Set-SingleQuoteString -InputString $conditionString
    Write-Output -InputObject (" condition='$($conditionString)'")
}

Function New-MetadataXML
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $TemplateName,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Description,

        [Parameter()]
        [guid]
        $ID = (New-Guid).Guid,

        [Parameter()]
        [version]
        $TemplateVersion = "1.0.0",

        [Parameter()]
        [string]
        $Author = $env:USERNAME,

        [Parameter()]
        [string]
        $Title,

        [Parameter()]
        [string[]]
        $Tags
    )

    If (-not($Title))
    {
        $Title = $TemplateName
    }

    $Tags = $Tags -join ', '


    $metadata = @"
<metadata>
    <name>$($TemplateName)</name>
    <id>$($ID)</id>
    <version>$($TemplateVersion)</version>
    <title>$($Title)</title>
    <description>$($Description)</description>
    <author>$($Author)</author>
    <tags>$($Tags)</tags>
</metadata>
 
"@

    Write-Output -InputObject $metadata
}
Function New-PlasterContentBaseFile
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [AllowEmptyString()]
        [string]
        $Source,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 1
        )]
        [string]
        $Destination,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [scriptblock]
        $Condition,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [switch]
        $OpenInEditor,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Default','Ascii','BigEndianUnicode','BigEndianUTF32','Oem','Unicode','UTF32','UTF7','UTF8','UTF8-NoBOM')]
        [string]
        $Endcoding,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('file', 'templateFile')]
        [string]
        $FileType
    )

    $fileSB = [System.Text.StringBuilder]::new("<$FileType")
    $fileSB.AppendLine(" source='$($Source)'") > $null
    $fileSB.AppendLine(" destination='$($Destination)'") > $null

    if ($Condition)
    {
        $conditionString = New-Condition -Condition $Condition
        $fileSB.AppendLine(" $conditionString") > $null
    }

    if ($OpenInEditor)
    {
        $fileSB.AppendLine(" OpenInEditor='true'") > $null
    }

    if($Endcoding)
    {
        $fileSB.AppendLine(" endcoding='$($Endcoding)'") > $null
    }

    $fileSB.Append('/>') > $null
    Write-Output -InputObject $fileSB.ToString().Trim(" ")
}
Function New-PlasterContentNewModuleManifestXML
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true
        )]
        [scriptblock]
        $Condition,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [string]
        $Destination,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $Author,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $Description,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $CompanyName,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $ModuleVersion,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $RootModule,


        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Default','Ascii','BigEndianUnicode','BigEndianUTF32','Oem','Unicode','UTF32','UTF7','UTF8','UTF8-NoBOM')]
        [string]
        $Encoding,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $OpenInEditor,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $PowershellVersion,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $NestedModules,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $DscResourcesToExport

    )

    $manifestSB = [System.Text.StringBuilder]::new("<newModuleManifest ")

    ForEach($parameter in $PSBoundParameters.Keys)
    {
        if($parameter -eq 'Condition')
        {
            $conditionString = New-Condition -Condition $Condition
            $manifestSB.AppendLine("condition=$conditionString") > $null
        }
        else
        {
            $propertyName = "{0}{1}" -f ($parameter[0]).ToString().ToLower(),$parameter.SubString(1)
            $manifestSB.AppendLine("$propertyName='$($PSBoundParameters[$parameter])'") > $null
        }
    }

    $manifestSB.Append("/>") > $null
    Write-Output -InputObject $manifestSB.ToString()
}
Function New-PlasterParameterBaseChoice
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Prompt,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Default,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Text','Encrypted')]
        [string]
        $Store,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [scriptblock]
        $ScriptBlock,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('choice','multichoice')]
        [string]
        $ChoiceType
    )

    $paramSB = [System.Text.StringBuilder]::new("<parameter name='$($Name)' type = '$ChoiceType'")
    if($Prompt)
    {
        $paramSB.Append(" prompt='$($Prompt)'") > $null
    }

    if($Default)
    {
        $paramSB.Append(" default='$($Default)'") > $null
    }

    if($Store)
    {
        $paramSB.Append(" store='$($Store)'") > $null
    }
    $paramSB.AppendLine(">") > $null

    $choices = $ScriptBlock.Invoke() | 
        Out-String
    
    $paramSB.Append($choices) > $null
    $paramSB.Append("</parameter>") > $null
    Write-Output $paramSB.ToString()
}
Function New-PlasterParameterBaseText
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $Prompt,

        [Parameter(
            ValueFromPipeline = $true,
            Position = 2
        )]
        [string]
        $Default,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('Text','Encrypted')]
        [string]
        $Store,

        [Parameter(
            ValueFromPipeline = $true
        )]
        [ValidateSet('text','user-fullname','user-email')]
        [string]
        $TextType
    )

    $paramSB = [System.Text.StringBuilder]::new("<parameter name='$($Name)' type = '$TextType' ")
    if($Prompt)
    {
        $paramSB.Append(" prompt='$($Prompt)'") > $null
    }

    if($Default)
    {
        $paramSB.Append(" default='$($Default)'") > $null
    }

    if($Store)
    {
        $paramSB.Append(" store='$($Store)'") > $null
    }
    
    $paramSB.AppendLine(" />") > $null
    Write-Output $paramSB.ToString()
}
Function Set-SingleQuoteString
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Position = 1
        )]
        [string]
        $InputString
    )

    $tokens = $null
    $errors = $null
    $ast = [System.Management.Automation.Language.Parser]::ParseInput($InputString,[ref]$tokens,[ref]$errors)
    $singleQuotes = $ast.FindAll({$args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst]},$true)
    foreach($singleQuote in $singleQuotes)
    {
        $oldValue = $singleQuote.Extent.Text
        $newValue = '"{0}"' -f $singleQuote.Value
        $InputString = $InputString.ToString().Replace($oldValue,$newValue)
    }

    Write-Output $InputString

}
function ShouldBeErrorId
{
    param([Parameter(ValueFromPipeline, Mandatory)]
        [ScriptBlock]
        $sb,

        [Parameter(Mandatory, Position=0)]
        [string]
        $FullyQualifiedErrorId)

        try
        {
            & $sb | Out-Null
            Throw "No Exception!"
        }
        catch
        {
            $_.FullyQualifiedErrorId | Should Be $FullyQualifiedErrorId | Out-Null
            # Write the exception to output that allow us to check later other properies of the exception
            Write-Output $_
        }
}