|
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) } |
|
} |