diff --git a/powershell/get-metadata.ps1 b/powershell/get-metadata.ps1 index 58375b7..115c961 100644 --- a/powershell/get-metadata.ps1 +++ b/powershell/get-metadata.ps1 @@ -1,110 +1,160 @@ -[cmdletbinding()] -Param( - [Parameter( - Mandatory= $true - )] - [ValidateScript({Test-Path $_ -PathType Leaf})] - [string]$Path +param ( + [Parameter(Mandatory)] + [ValidateScript({ + Test-Path $_ -PathType Leaf + })] + [string]$Path ) -begin -{ - $system_types = @('Parameter','ValidatePattern','ValidateScript','ValidateSet') -} - -process -{ - $script_path_raw = Get-Item $Path +process { +Add-Type -AssemblyName System.Management.Automation +$script_path_raw = Get-Item $Path $script_name = $script_path_raw.Name $script_path = $script_path_raw | Select-Object -ExpandProperty FullName +$tokens = $null +$errors = $null +$ast = [System.Management.Automation.Language.Parser]::ParseFile($script_path, [ref]$tokens, [ref]$errors) - $parse_errors = @() - - $AST = [System.Management.Automation.Language.Parser]::ParseFile($script_path, [ref]$null, [ref]$parse_errors) - - if($null -eq $AST.ParamBlock) - { - throw "No 'Param(...)' block found: $($Path)" - } - - $parameters = @() - - foreach($param in $AST.ParamBlock.Parameters) - { - $param_name = $param.Name.Extent.Text - $param_type = $null - $param_mandatory = $false - $param_parameters = @() - $param_validations = @() - - foreach($attr in $param.Attributes) - { - if($system_types -contains $attr.TypeName.Name) - { - - switch -Wildcard ($attr.TypeName.Name) - { - "Parameter" - { - foreach($named_argument in $attr.NamedArguments) - { - if($named_argument.ArgumentName -eq 'Mandatory') - { - $param_mandatory = $named_argument.Argument.Extent.Text -eq '$true' - } else - { - - $param_parameters += [pscustomobject]@{ - name = $named_argument.ArgumentName - value = $named_argument.Argument.Extent.Text - } - } - } - } - "Validate*" - { - foreach($positional_argument in $attr.PositionalArguments) - { - if($positional_argument.ScriptBlock) - { - $value = $positional_argument.ScriptBlock.Extent.Text - } else - { - $value = $positional_argument.Value - } - $param_validations += - [pscustomobject]@{ - name = $attr.TypeName.Name - value = $value - } - } - } - } - } else - { - if($attr.Extent.Text -like '`[*`]') - { - if($param_type -ne $null) - { - Write-Warning "Multiple Types found for Parameter: $($attr.TypeName.Name) ($($Path))" - continue - } - $param_type = $attr.TypeName.Name - } - } - } - $parameters += [pscustomobject]@{ - name = $param_name - type = $param_type - mandatory = $param_mandatory - arguments = $param_parameters - validations = $param_validations - } - - } - return [pscustomobject]@{ - name = $script_name - path = $script_path - parameters = $parameters - } +if ($errors.Count -gt 0) { + throw "Parse errors found: $($errors | ForEach-Object { $_.Message })" +} + +$paramBlock = $ast.ParamBlock +if (-not $paramBlock) { + throw "No param() block found at the script level." +} + +$cmdletBindingAttr = $paramBlock.Attributes | Where-Object { $_.TypeName.Name -eq 'CmdletBinding' } + +$defaultParamSet = $cmdletBindingAttr.NamedArguments | + Where-Object { $_.ArgumentName -eq 'DefaultParameterSetName' } | + Select-Object -ExpandProperty Argument | + Select-Object -ExpandProperty Value + +$parameters = @() + +foreach ($param in $paramBlock.Parameters) { + $paramName = $param.Name.VariablePath.UserPath + + # Fix the type name + $staticType = $param.StaticType + if ($staticType.IsGenericType -and $staticType.Name -eq 'Nullable`1') { + $innerType = $staticType.GenericTypeArguments[0].Name + $paramType = "Nullable[$innerType]" + } else { + $paramType = $staticType.Name + } + + $aliases = @() + $validations = @() + $parameterSets = @() + $description = $null + $helpMessage = $null + + foreach ($attr in $param.Attributes) { + switch ($attr.TypeName.Name) { + 'Parameter' { + + $mandatory = $false + $parametersetname = 'Default' + $position = $null + $frompipeline = $null + $frompipeline_by_property = $null + $helpmessage = $null + + foreach ($arg in $attr.NamedArguments) { + switch ($arg.ArgumentName) { + 'Mandatory' { + $mandatory = $arg.Argument.Extent.Text -eq '$true' + } + 'Position' { + $position = $arg.Argument.Value + } + 'ParameterSetName' { + $parametersetname = $arg.Argument.Value + } + 'ValueFromPipeline' { + $frompipeline = $arg.Argument.Extent.Text -eq '$true' + } + 'ValueFromPipelineByPropertyName' { + $frompipeline_by_property = $arg.Argument.Extent.Text -eq '$true' + } + 'HelpMessage' { + $helpMessage = $arg.Argument.Value + } + } + } + + $paramInfo = @{ + name = $parametersetname + mandatory = $mandatory + } + + if($null -ne $position) { + $paramInfo.position = $position + } + + if($null -ne $frompipeline) { + $paramInfo.valueFromPipeline = $frompipeline + } + + if($null -ne $frompipeline_by_property) { + $paramInfo.valueFromPipelineByPropertyName = $frompipeline_by_property + } + + $parameterSets += [pscustomobject]$paramInfo + break + } + 'Alias' { + $aliases += $attr.PositionalArguments.Value + break + } + { $_ -like 'Validate*' } { + $val = @{ + type = $attr.TypeName.Name + } + + if ($attr.TypeName.Name -eq 'ValidateRange') { + $val.min = $attr.PositionalArguments[0].Value + $val.max = $attr.PositionalArguments[1].Value + } elseif ($attr.TypeName.Name -eq 'ValidateSet') { + $val.values = $attr.PositionalArguments.Value + } elseif ($attr.TypeName.Name -eq 'ValidateScript') { + $val.script = $attr.PositionalArguments.ScriptBlock.Extent.Text + } + + $validations += [pscustomobject]$val + break + } + } + } + + if ($parameterSets.Count -eq 0) { + $parameterSets += [pscustomobject]@{ + name = "Default" + mandatory = $false + } + } + + $parameters += [pscustomobject]@{ + name = $paramName + type = $paramType + aliases = $aliases + description = $description + helpMessage = $helpMessage + validations = $validations + parameterSets = $parameterSets + } +} + +$result = [pscustomobject]@{ + name = $script_name + path = $script_path + parameters = $parameters + cmdletBinding = [pscustomobject]@{ + defaultParameterSetName = $defaultParamSet ?? "Default" + } +} + +return $result }