|
using namespace System.Collections.Generic
|
|
|
|
class Range {
|
|
<#
|
|
.SYNOPSIS
|
|
A class to hold the start/end data for a given contiguous integer range.
|
|
#>
|
|
[long]$Start
|
|
[long]$End
|
|
|
|
# Default New Values
|
|
Range() {
|
|
$this.Start = ([int]::MinValue - 1)
|
|
$this.End = ([int]::MaxValue + 1)
|
|
}
|
|
# Set Values
|
|
Range([long]$Start, [long]$End) {
|
|
$this.Start = $Start
|
|
$this.End = $End
|
|
}
|
|
|
|
[string]ToString() {
|
|
return (
|
|
if ($this.Start -eq $this.End) {
|
|
$this.Start
|
|
} else {
|
|
"$($this.Start)-$($this.End)"
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
function Get-ContiguousRange {
|
|
<#
|
|
.SYNOPSIS
|
|
Finds contiguous ranges of integers within a given integer array.
|
|
|
|
.DESCRIPTION
|
|
This function takes a list of integers and identifies contiguous ranges within the list.
|
|
The output can be a list of Range objects or a string.
|
|
|
|
.PARAMETER IntRange
|
|
Array of integers to find contiguous ranges within
|
|
|
|
.PARAMETER CombineString
|
|
If selected, returns the contiguous ranges as a single comma separated string.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([List[Range]], [string])]
|
|
param (
|
|
[Parameter(
|
|
Position = 0,
|
|
Mandatory,
|
|
HelpMessage = 'Enter one or more integers.'
|
|
)]
|
|
[int[]]$IntRange,
|
|
|
|
[Parameter(
|
|
Position = 1,
|
|
Mandatory = $false,
|
|
HelpMessage = 'Outputs contiguous ranges as a string.'
|
|
)]
|
|
[switch]$CombineString
|
|
)
|
|
|
|
begin {
|
|
$Return = [List[Range]]::new()
|
|
$RangeObj = [Range]::New()
|
|
[long]$Index = 0
|
|
[long[]]$LongRange = $IntRange | Sort-Object -Unique
|
|
}
|
|
|
|
process {
|
|
for ([long]$a = $LongRange[0]; [long]$a -le $LongRange[-1]; [long]$a++) {
|
|
# Set Start value
|
|
if ($RangeObj.Start -eq ([int]::MinValue - 1)) {
|
|
if ($LongRange[$Index] -ne $a) { $a = $LongRange[$Index] }
|
|
$RangeObj.Start = $a
|
|
}
|
|
|
|
# Set End Value
|
|
if (
|
|
($LongRange[$Index] -eq $a) -and
|
|
($LongRange[($Index + 1)] -ne ([long]$a + 1)) -and
|
|
($RangeObj.End -eq ([int]::MaxValue + 1))
|
|
) {
|
|
$RangeObj.End = $a
|
|
$Return.Add($RangeObj)
|
|
$RangeObj = [Range]::New()
|
|
}
|
|
$Index++
|
|
}
|
|
}
|
|
|
|
end {
|
|
if ($CombineString) {
|
|
$Strings = $Return | ForEach-Object { $_.ToString() }
|
|
return $Strings -join ', '
|
|
} else {
|
|
return $Return
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class SemVer {
|
|
<#
|
|
.SYNOPSIS
|
|
Translates a version string into a formal semantic versioning pattern.
|
|
{Major}.{Minor}.{Patch}-{pre-releaseTag}+{buildNum}
|
|
|
|
.DESCRIPTION
|
|
Utilizes regular expressions to verify if a given string adheres to semantic versioning rules.
|
|
For more details: https://semver.org/
|
|
|
|
Examples of valid semantic versions:
|
|
ex0: 2.1.4
|
|
ex1: 5.12.96-pr1+000a
|
|
ex2: 10.3.2+002
|
|
ex3: 3.1.0-rc3
|
|
|
|
.PARAMETER Version
|
|
The version string to be parsed into a SemVer object.
|
|
|
|
.OUTPUTS
|
|
When the provided version does not follow semantic versioning format, the Valid field will be set to false.
|
|
The Major, Minor, and Patch properties will be initialized to 0 and the PreRelease and Build properties will
|
|
contain empty strings.
|
|
|
|
Conversely, if the version adheres to the semantic versioning format, the Valid field will be set to true.
|
|
The Major, Minor, and Patch properties will contain their respective values as [long]s.
|
|
The PreRelease and Build properties will either be empty if unused, or hold the string value of the relevant fields.
|
|
#>
|
|
[boolean]$Valid = $false
|
|
[long]$Major = 0
|
|
[long]$Minor = 0
|
|
[long]$Patch = 0
|
|
[string]$PreRelease = [string]::Empty
|
|
[string]$Build = [string]::Empty
|
|
|
|
SemVer( [string]$Version ) {
|
|
$SemVerRegex = '^' +
|
|
'(?<Major>0|[1-9]\d*)\.' +
|
|
'(?<Minor>0|[1-9]\d*)\.' +
|
|
'(?<Patch>0|[1-9]\d*)' +
|
|
'(?:-(?<PreRelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))' +
|
|
'?(?:\+(?<Build>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?' +
|
|
'$'
|
|
if ($Version -match $SemVerRegex) {
|
|
$this.Valid = $true
|
|
$this.Major = [long]::Parse($Matches['Major'])
|
|
$this.Minor = [long]::Parse($Matches['Minor'])
|
|
$this.Patch = [long]::Parse($Matches['Patch'])
|
|
$this.PreRelease = $Matches['PreRelease']
|
|
$this.Build = $Matches['Build']
|
|
}
|
|
}
|
|
|
|
[string] ToString() {
|
|
$result = [string]::Empty
|
|
if ($this.Valid) {
|
|
$result = "$($this.Major).$($this.Minor).$($this.Patch)"
|
|
if ([string]::IsNullOrEmpty($this.PreRelease) -eq $false) {
|
|
$result += "-$($this.PreRelease)"
|
|
}
|
|
if ([string]::IsNullOrEmpty($this.Build) -eq $false) {
|
|
$result += "+$($this.Build)"
|
|
}
|
|
}
|
|
return $result
|
|
}
|
|
}
|
|
|
|
|
|
function Test-MatchesSemVer {
|
|
<#
|
|
.SYNOPSIS
|
|
Evaluates if the provided version string adheres to semantic versioning rules.
|
|
|
|
.DESCRIPTION
|
|
This function checks whether the input string follows the semantic versioning format and returns the appropriate
|
|
boolean value or the [SemVer] object if PassThru is selected.
|
|
|
|
.PARAMETER Version
|
|
The version string to be evaluated against semantic versioning rules.
|
|
|
|
.PARAMETER PassThru
|
|
An optional switch parameter. When selected, returns the [SemVer] object instead of a boolean.
|
|
|
|
.OUTPUTS
|
|
If the version string does not adhere to semantic versioning format, the function returns false.
|
|
|
|
When the version string complies with semantic versioning rules, the function returns true, unless PassThru is
|
|
selected. If PassThru is selected and the version string is valid, it returns the [SemVer] object.
|
|
|
|
.EXAMPLE
|
|
if (Test-MatchesSemVer -Version $Version) {
|
|
Move-Item -Path $Csproj.FullName -Destination $PackagePath
|
|
}
|
|
|
|
.EXAMPLE
|
|
$SemVer = Test-MatchesSemVer -Version $Version -PassThru
|
|
if ($SemVer.Valid -and ($SemVer.PreRelease -eq [string]::Empty)) {
|
|
dotnet nuget push $Package.FullName --api-key $ApiKey --source $NugetSource
|
|
}
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([Boolean], [SemVer])]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$Version,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$PassThru
|
|
)
|
|
|
|
[SemVer]$SemVer = [SemVer]::new($Version)
|
|
if ($SemVer.Valid) {
|
|
if ($PassThru) { return $SemVer } else { return $true }
|
|
}
|
|
return $false
|
|
}
|
|
|
|
|
|
function Write-ReverseString {
|
|
<#
|
|
.SYNOPSIS
|
|
Reverses a given string.
|
|
|
|
.DESCRIPTION
|
|
This function takes a string as input and reverses its characters. The output is the reversed string.
|
|
|
|
.PARAMETER String
|
|
The string to be reversed.
|
|
|
|
.PARAMETER Encoding
|
|
The encoding method to use when reversing the string.
|
|
Available options are 'Utf8' and 'Utf16'. The default is 'Utf8'.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([string])]
|
|
param (
|
|
[Parameter(
|
|
Position = 0,
|
|
Mandatory,
|
|
ValueFromPipeline
|
|
)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]$String,
|
|
|
|
[Parameter(
|
|
Position = 1,
|
|
Mandatory = $false
|
|
)]
|
|
[ValidateSet( 'Utf8', 'Utf16')]
|
|
[string]$Encoding = 'Utf8'
|
|
)
|
|
begin { $chars = [List[char]]::new() }
|
|
process {
|
|
$runes = @($String.EnumerateRunes())
|
|
for ($i = ($runes.Count - 1); $i -ge 0; $i--) {
|
|
$rune = $runes[$i]
|
|
|
|
switch ($Encoding) {
|
|
'Utf8' {
|
|
$points = [char[]]::new($rune.Utf8SequenceLength)
|
|
$rune.EncodeToUtf8($points) | Out-Null
|
|
}
|
|
'Utf16' {
|
|
$points = [char[]]::new($rune.Utf16SequenceLength)
|
|
$rune.EncodeToUtf16($points) | Out-Null
|
|
}
|
|
Default { throw "'$Encoding' is not a valid encoding option." }
|
|
}
|
|
$chars.AddRange($points)
|
|
}
|
|
}
|
|
end { return [string]::new($chars) }
|
|
}
|