private/Debug-SemanticVersion.ps1

function Debug-SemanticVersion {
    <#
     .SYNOPSIS
        Finds problems with a Semantic Version string and recommends solutions.
 
     .DESCRIPTION
        The Debug-SemanticVersion function finds problems with a Semantic Version string and recommends solutions.
 
        It is used by other functions in the SemanticVersion module to get the appropriate error message when a
        Semantic Version string is invalid.
 
     .EXAMPLE
        An example
 
     .NOTES
        General notes
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param (
        # The object to debug. Object will be converted to a string for evaluation.
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true)]
        [object[]]
        [Alias('Version')]
        $InputObject,

        # The name of the parameter that is being debugged/validated. If specified, the name will be added to the returned exception and error details objects.
        [string]
        $ParameterName = 'InputObject'
    )

    begin {
        # Default values.
        [System.Management.Automation.ErrorCategory] $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
    }

    process {
        foreach ($item in $InputObject) {
            [string] $version = $item -as [string]
            [bool] $isValid = $version -match ('^' + $SemanticVersionPattern + '$')
            [string] $messageId = ''
            [string] $message = ''
            [string] $recommendedAction = ''
            [hashtable] $outputHash = @{
                Message = ''
            }

            if ($isValid) {
                $messageId = 'ValidSemanticVersion'
                $message = $messages[$messageId] -f $version
                $outputHash['Message'] = $message
                $outputHash['RecommendedAction'] = $recommendedAction
            }
            else {
                $messageId = 'InvalidSemanticVersion'
                $message = $messages[$messageId] -f $version
                $recommendedAction = $messages[$messageId + 'RecommendedAction']

                [System.ArgumentException] $ex = New-Object -TypeName System.ArgumentException -ArgumentList @($message, $ParameterName)
                $outputHash['Exception'] = $ex
                $outputHash['Category'] = $errorCategory
                $outputHash['TargetObject'] = $item
                $outputHash['CategoryActivity'] = 'Debug-SemanticVersion'
                $outputHash['CategoryTargetName'] = $ParameterName
                $outputHash['CategoryTargetType'] = $item.GetType()
                $outputHash['CategoryReason'] = $messageId

                [string] $normalVersion = ''
                [string] $prereleaseLabel = ''
                [string] $buildLabel = ''

                # Try to split the string into the standard semver parts in order to find out why it is invalid.
                # normalVersion-preRelease+build
                $elementCountSplit = $version -split '\.'
                if ($elementCountSplit.Length -eq 3) {
                    $normalVersion = $version
                    $prereleaseLabel = ''
                    $buildLabel = ''
                }
                elseif ($version.Contains('-') -and $version.Contains('+')) {
                    $normalVersion = @($version -split '\-', 2)[0]
                    $prereleaseLabel = @(@($version -split '\-', 2)[-1] -split '\+', 2)[0]
                    $buildLabel = @(@($version -split '\-', 2)[-1] -split '\+', 2)[-1]
                }
                # normalVersion-preRelease
                elseif ($version.Contains('-') -and -not $version.Contains('+')) {
                    $normalVersion = @($version -split '\-', 2)[0]
                    $prereleaseLabel = @($version -split '\-', 2)[-1]
                    $buildLabel = ''
                }
                # normalVersion+build
                elseif (-not $version.Contains('-') -and $version.Contains('+')) {
                    $normalVersion = @($version -split '\+', 2)[0]
                    $prereleaseLabel = ''
                    $buildLabel = @($version -split '\+', 2)[-1]
                }
                # normalVersion
                else {
                    $normalVersion = $version
                    $prereleaseLabel = ''
                    $buildLabel = ''
                }

                Write-Debug "`$normalVersion: $normalVersion"
                Write-Debug "`$prereleaseLabel: $prereleaseLabel"
                Write-Debug "`$buildLabel: $buildLabel"

                # Validate normal version.
                if ($normalVersion -notmatch ('^' + $NormalVersionPattern + '$')) {
                    $messageId = 'InvalidNormalVersion'
                    $message = $messages[$messageId]
                    $recommendedAction = $messages[$messageId + 'RecommendedAction']

                    [string[]] $normalVersionElements = @($normalVersion -split '\.')
                    if ($normalVersionElements.Length -ne 3) {
                        $messageId = 'InvalidNormalVersionElementCount'
                        $message = $messages[$messageId] -f $normalVersion, $normalVersionElements.Length
                        $recommendedAction = $messages[$messageId + 'RecommendedAction']
                    }
                    else {
                        for ($i = 0; $i -lt $normalVersionElements.Length; $i++) {
                            switch ($i) {
                                0 {$elementName = 'Major'}
                                1 {$elementName = 'Minor'}
                                2 {$elementName = 'Patch'}
                            }

                            if ($normalVersionElements[$i] -match ('^' + $NormalVersionElementPattern + '$')) {
                                continue
                            }
                            elseif ($normalVersionElements[$i].Trim() -eq '') {
                                $messageId = 'NormalVersionElementIsEmpty'
                                $message = $messages[$messageId] -f $elementName
                                $recommendedAction = $messages[$messageId + 'RecommendedAction'] -f $elementName
                                break
                            }
                            #elseif ($normalVersionElements[$i] -as [int] -as [string] -ne $normalVersionElements[$i]) {
                            else {
                                #$message = '{0} version must not contain leading zeros.' -f $elementName
                                $messageId = 'CannotConvertNormalVersionElementToInt'
                                $message = $messages[$messageId] -f $elementName
                                $recommendedAction = $messages[$messageId + 'RecommendedAction']
                                break
                            }
                        }
                    }
                }
                # Validate pre-release.
                elseif ($prereleaseLabel.Length -ne 0 -and $prereleaseLabel -notmatch ('^' + $PreReleasePattern + '$')) {
                    $messageId = 'InvalidMetadataLabel'
                    $message = $messages[$messageId] -f $textInfo.ToTitleCase($messages['PreReleaseLabelName'])
                    $recommendedAction = $messages[$messageId + 'RecommendedAction'] -f $messages['PreReleaseLabelName']

                    [string[]] $prereleaseIdentifers = @($prereleaseLabel -split '\.')
                    for ($i = 0; $i -lt $prereleaseIdentifers.Length; $i++) {
                        if ($prereleaseIdentifers[$i] -match ('^' + $PreReleaseIdentifierPattern + '$')) {
                            continue
                        }
                        elseif ($prereleaseIdentifers[$i].Trim() -eq '') {
                            $messageId = 'MetadataIdentifierIsEmpty'
                            $message = $messages[$messageId] -f $textInfo.ToTitleCase($messages['PreReleaseLabelName']), $i
                            $recommendedAction = $messages[$messageId + 'RecommendedAction'] -f $messages['PreReleaseLabelName']
                        }
                        else {
                            $messageId = 'InvalidPreReleaseIdentifier'
                            $message = $messages[$messageId] -f $i
                            $recommendedAction = $messages[$messageId + 'RecommendedAction']
                        }
                    }
                }
                # Validate build.
                elseif ($buildLabel.Length -ne 0 -and $buildLabel -notmatch ('^' + $BuildPattern + '$')) {
                    $messageId = 'InvalidMetadataLabel'
                    $message = $messages[$messageId] -f $textInfo.ToTitleCase($messages['BuildLabelName'])
                    $recommendedAction = $messages[$messageId + 'RecommendedAction'] -f $messages['BuildLabelName']

                    [string[]] $buildIdentifers = @($buildLabel -split '\.')
                    for ($i = 0; $i -lt $buildIdentifers.Length; $i++) {
                        if ($buildIdentifers[$i] -match ('^' + $BuildIdentifierPattern + '$')) {
                            continue
                        }
                        elseif ($buildIdentifers[$i].Trim() -eq '') {
                            $messageId = 'MetadataIdentifierIsEmpty'
                            $message = $messages[$messageId] -f $textInfo.ToTitleCase($messages['BuildLabelName']), $i
                            $recommendedAction = $messages[$messageId + 'RecommendedAction'] -f $messages['BuildLabelName']
                        }
                        else {
                            $messageId = 'InvalidBuildIdentifier'
                            $message = $messages[$messageId] -f $i
                            $recommendedAction = $messages[$messageId + 'RecommendedAction']
                        }
                    }
                }

                $outputHash['CategoryReason'] = $messageId
                $outputHash['ErrorId'] = $messageId
                $outputHash['Message'] = $message
                $outputHash['RecommendedAction'] = $recommendedAction
            }

            $outputHash
        }
    }
}