Skip to content

Instantly share code, notes, and snippets.

@altrive
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save altrive/43e3a149259e9c681efb to your computer and use it in GitHub Desktop.
Save altrive/43e3a149259e9c681efb to your computer and use it in GitHub Desktop.
Enable [OutputType] validation
Set-OutputTypeValidationMode
function Main
{
Test-ShouldReturnInt32
Test-ShouldReturnVoid
}
#region function definitions
function Test-ShouldReturnInt32
{
[OutputType([int])]
param ()
#Warning: Should return [int]
return [long] 1
}
function Test-ShouldReturnVoid
{
[OutputType([void])]
param ()
#Warning: Should not return object
return 1
}
Main
function Set-OutputTypeValidationMode
{
[OutputType([void])]
[CmdletBinding(DefaultParameterSetName = 'Strict')]
param (
#[Parameter(Mandatory, ParameterSetName = 'Off')]
#[switch] $Off,
#[Parameter(ParameterSetName = 'Strict')]
#[switch] $Strict = $false,
#[switch] $Exclude
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$ExecutionContext.InvokeCommand.PostCommandLookupAction = {
param (
$CommandName,
$CommandLookupEventArgs
)
#Don't need to handle internal command
if ($CommandLookupEventArgs.CommandOrigin -eq [System.Management.Automation.CommandOrigin]::Internal){
return
}
#Skip prompt command
if ($CommandName -eq 'prompt'){
return
}
#Retrive OutputType informations from command
[Type[]] $outputTypes = [Linq.Enumerable]::ToArray($CommandLookupEventArgs.Command.OutputType).Type | where { $_ -ne $null }
[string[]] $outputTypeNames = [Linq.Enumerable]::ToArray($CommandLookupEventArgs.Command.OutputType).Name
#If [OutputType] attribute is not specified. skip validation
if ($null -eq $outputTypeNames){
return
}
#TODO:Support function parametersets
#TODO:Support -Passthru parameter
#$supportPassThru = $CommandLookupEventArgs.Command.Parameters.ContainsKey('Passthru')
#TODO: Need to handle ParameterSetName overloads([OutputType([<TypeLiteral>], ParameterSetName="<Name>")])
[bool] $shouldReturnVoid = ($outputTypeNames[0] -eq [void].FullName) #Explicitly declare funtion with [OutputType([void])]
[bool] $shouldNotReturnVoid = ($outputTypeNames[0] -ne [void].FullName) #Don't contains [OutputType([void])] declaration
#$CommandLookupEventArgs.StopSearch = $true
$CommandLookupEventArgs.CommandScriptBlock = {
[long] $count = 0
#TODO:Convert to filter instead of foreach-object
& $CommandName @args | foreach {
#Write object to output stream
Write-Output -InputObject $_
$returnObjectType = $_.GetType()
++$count
$isValid = ($outputTypes | foreach { $_.IsAssignableFrom($returnObjectType)}) -eq $true
if (!$isValid)
{
Write-Warning ("FunctionName = {0}`r`n`tReturn = {1}`r`n`tExpected = {2}" -f $CommandName, $returnObjectType.FullName, [String]::Join(', ', $outputTypeNames))
#TODO: Need to suppress warning for large pipeline inputs
}
}
if ($shouldReturnVoid -and ($count -gt 0)){
Write-Warning ("Function should not return object!,CommandName={0}" -f $CommandName)
return
}
if ($shouldNotReturnVoid -and ($count -eq 0)){
Write-Warning ("Function should return object!,CommandName={0}" -f $CommandName)
return
}
#Output Validation is successfully completed.
}.GetNewClosure()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment