Private/Utilities.psm1
|
using module .\Colors.psm1 using module .\Models.psm1 using module .\Console.psm1 class FileTools { static [PSObject] GetItemSize([string]$Path) { $size = 0 if (Test-Path $Path -PathType Container -ErrorAction SilentlyContinue) { $size = Get-ChildItem -Path $Path -File -Recurse -Force | Measure-Object -Property Length -Sum | Select-Object -ExpandProperty sum } else { $size = Get-Item -Path $Path | Select-Object -ExpandProperty Length } return [PSCustomObject] @{ bytes = $size; Item = Get-Item $Path } } static [string] GetShortPath([string]$Path, [int]$KeepBefore, [int]$KeepAfter, [string]$Separator, [string]$TruncateChar) { $splitPath = $Path.Split($Separator, [System.StringSplitOptions]::RemoveEmptyEntries) if ($splitPath.Count -gt ($KeepBefore + $KeepAfter)) { $outPath = [string]::Empty for ($i = 0; $i -lt $KeepBefore; $i++) { $outPath += $splitPath[$i] + $Separator } $outPath += "$($TruncateChar)$($Separator)" for ($i = ($splitPath.Count - $KeepAfter); $i -lt $splitPath.Count; $i++) { if ($i -eq ($splitPath.Count - 1)) { $outPath += $splitPath[$i] } else { $outPath += $splitPath[$i] + $Separator } } } else { $outPath = $splitPath -join $Separator if ($splitPath.Count -eq 1) { $outPath += $Separator } } return $outPath } static [string] GetShortPath([string]$Path) { return [FileTools]::GetShortPath($Path, 2, 1, [System.IO.Path]::DirectorySeparatorChar, [char]8230) } static [string] NewRandomFileName([string]$Extension, [bool]$UseTempFolder, [bool]$UseHomeFolder) { if ($UseTempFolder) { $filename = [system.io.path]::GetTempFileName() } elseif ($UseHomeFolder) { $homedocs = [Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments) $filename = Join-Path -Path $homedocs -ChildPath ([system.io.path]::GetRandomFileName()) } else { $filename = [system.io.path]::GetRandomFileName() } if (![string]::IsNullOrEmpty($Extension)) { $original = [system.io.path]::GetExtension($filename).Substring(1) return $filename -replace "$original$", $Extension } return $filename } static [string] GetTempDirectory() { return [System.IO.Path]::GetTempPath() } static [string] GetHomeDirectory() { return [Environment]::GetFolderPath([Environment+SpecialFolder]::UserProfile) } } class HashTools { static [hashtable] JoinHashtable([hashtable]$First, [hashtable]$Second, [bool]$Force) { $Primary = $First.Clone() $Secondary = $Second.Clone() $duplicates = $Primary.keys | Where-Object { $Secondary.ContainsKey($_) } if ($duplicates) { foreach ($item in $duplicates) { if ($Force) { $Secondary.Remove($item) } else { $r = Read-Host "Which key do you want to KEEP [AB]?" if ($r -eq "A") { $Secondary.Remove($item) } elseif ($r -eq "B") { $Primary.Remove($item) } else { Write-Warning "Aborting"; return $null } } } } return $Primary + $Secondary } } class HostTools { static [int] GetHostHeight() { return (Get-Host).UI.RawUI.BufferSize.Height } static [int] GetHostWidth() { return ([type]'ConsoleWriter')::get_ConsoleWidth() } static [string] GetHostOs() { return [cryptobase]::GetHostOs() } static [object] InvokeInputBox([string]$Title, [string]$Prompt, [bool]$AsSecureString, [string]$BackgroundColor) { if ((Test-IsPSWindows)) { Add-Type -AssemblyName 'PresentationFramework' Add-Type -AssemblyName 'PresentationCore' Remove-Variable -Name myInput -Scope script -ErrorAction SilentlyContinue $form = New-Object System.Windows.Window $stack = New-Object System.Windows.Controls.StackPanel $form.Title = $Title $form.Height = 150 $form.Width = 350 $form.Background = $BackgroundColor $label = New-Object System.Windows.Controls.Label $label.Content = " $Prompt" $label.HorizontalAlignment = "left" $stack.AddChild($label) if ($AsSecureString) { $inputbox = New-Object System.Windows.Controls.PasswordBox } else { $inputbox = New-Object System.Windows.Controls.TextBox } $inputbox.Width = 300 $inputbox.HorizontalAlignment = "center" $stack.AddChild($inputbox) $space = New-Object System.Windows.Controls.Label $space.Height = 10 $stack.AddChild($space) $btn = New-Object System.Windows.Controls.Button $btn.Content = "_OK" $btn.Width = 65 $btn.HorizontalAlignment = "center" $btn.VerticalAlignment = "bottom" $btn.Add_click({ if ($AsSecureString) { $script:myInput = $inputbox.SecurePassword } else { $script:myInput = $inputbox.text } $form.Close() }) $stack.AddChild($btn) $space2 = New-Object System.Windows.Controls.Label $space2.Height = 10 $stack.AddChild($space2) $btn2 = New-Object System.Windows.Controls.Button $btn2.Content = "_Cancel" $btn2.Width = 65 $btn2.HorizontalAlignment = "center" $btn2.VerticalAlignment = "bottom" $btn2.Add_click({ $form.Close() }) $stack.AddChild($btn2) $form.AddChild($stack) [void]$inputbox.Focus() $form.WindowStartupLocation = 1 [void]$form.ShowDialog() return $script:myInput } else { Write-Warning "Sorry. This command requires a Windows platform." return $null } } static [object] InvokeInputBox() { return [HostTools]::InvokeInputBox("User Input", "Please enter a value:", $false, "White") } } class ModuleTools { static [hashtable] GetModuleData() { $d = @{} Get-ChildItem -Path "$((Get-Module cliHelper.core).ModuleBase)/en-US" -File data*.csv | ForEach-Object { $d[$_.Name.Replace('data.', '').Replace('.csv', '')] = [IO.File]::ReadAllText($_.FullName) | ConvertFrom-Csv } return $d } } class ProgressUtil { static [PsRecord] $data = @{ ShowProgress = { return (Get-Variable 'VerbosePreference' -ValueOnly) -eq 'Continue' } ProgressBarColor = "LightSeaGreen" ProgressMsgColor = "LightGoldenrodYellow" ProgressBlock = '■' TwirlFrames = '' TwirlEmojis = [string[]]@( "◰◳◲◱", "◇◈◆", "◐◓◑◒", "←↖↑↗→↘↓↙", "┤┘┴└├┌┬┐", "⣾⣽⣻⢿⡿⣟⣯⣷", "|/-\\", "-\\|/", "|/-\\" ) } static [void] WriteProgressBar([int]$percent) { [ProgressUtil]::WriteProgressBar($percent, $true, "") } static [void] WriteProgressBar([int]$percent, [string]$message) { [ProgressUtil]::WriteProgressBar($percent, $true, $message) } static [void] WriteProgressBar([int]$percent, [bool]$update, [string]$message) { [ProgressUtil]::WriteProgressBar($percent, $update, [int]([ConsoleWriter]::get_ConsoleWidth() * 0.7), $message) } static [void] WriteProgressBar([int]$percent, [bool]$update, [string]$message, [bool]$Completed) { [ProgressUtil]::WriteProgressBar($percent, $update, [int]([ConsoleWriter]::get_ConsoleWidth() * 0.7), $message, $Completed) } static [void] WriteProgressBar([int]$percent, [bool]$update, [int]$PBLength, [string]$message) { [ProgressUtil]::WriteProgressBar($percent, $update, $PBLength, $message, $false) } static [void] WriteProgressBar([int]$percent, [bool]$update, [int]$PBLength, [string]$message, [bool]$Completed) { [ProgressUtil]::WriteProgressBar($percent, $update, $PBLength, $message, $Completed, [ProgressUtil]::data.ProgressBarcolor) } static [void] WriteProgressBar([int]$percent, [bool]$update, [int]$PBLength, [string]$message, [bool]$Completed, [string]$PBcolor) { <# .SYNOPSIS A simple progress utility class .EXAMPLE for ($i = 0; $i -le 100; $i++) { [ProgressUtil]::WriteProgressBar($i, "doing stuff") } #> if ([ProgressUtil]::data.ShowProgress) { [ValidateNotNull()][int]$PBLength = $PBLength; [ValidateNotNull()][int]$percent = $percent; $PmsgColor = [ProgressUtil]::data.ProgressMsgcolor [ValidateNotNull()][bool]$update = $update; [ValidateNotNull()][string]$message = $message; [ValidateScript( { return [bool][color]$_ })][string]$PmsgColor = $PmsgColor; [ValidateScript( { return [bool][color]$_ })][string]$PBcolor = $PBcolor; if ($update) { [Console]::Write(("`b" * [ConsoleWriter]::get_ConsoleWidth())) } Write-Console $message -f $PmsgColor -NoNewLine Write-Console " [" -f $PBcolor -NoNewLine $p = [int](($percent / 100.0) * $PBLength + 0.5) for ($i = 0; $i -lt $PBLength; $i++) { if ($i -ge $p) { Write-Console ' ' -NoNewLine } else { Write-Console ([ProgressUtil]::data.ProgressBlock) -f $PBcolor -NoNewLine } } Write-Console "] " -f $PBcolor -NoNewLine Write-Console ("{0,3:##0}%" -f $percent) -f $PmsgColor -NoNewLine:(!$Completed) } else { Write-Debug '[ProgressUtil]::data.ShowProgress is set to false. Progress bar will not be displayed. Please enable it by running [ProgressUtil]::ToggleShowProgress()' } } static [System.Management.Automation.Job] WaitJob([string]$progressMsg, [scriptblock]$sb) { return [ProgressUtil]::WaitJob($progressMsg, $sb, $null) } static [System.Management.Automation.Job] WaitJob([string]$progressMsg, [System.Management.Automation.Job]$Job) { return [ProgressUtil]::WaitJob($progressMsg, $Job, [ProgressUtil]::data.ProgressMsgcolor) } static [System.Management.Automation.Job] WaitJob([string]$progressMsg, [System.Management.Automation.Job]$Job, [string]$PmsgColor) { <# .DESCRIPTION waitjob is different from writeprogressbar - it's a visual progress bar that spins while waiting for a job to complete useful when we don't know the percentage of completion or it's not linear. we use it to create a better visual experience when waiting for long running operations. .EXAMPLE [ProgressUtil]::WaitJob("waiting", { Start-Sleep -Seconds 3 }); .EXAMPLE $j = [ProgressUtil]::WaitJob("Waiting", { Param($ob) Start-Sleep -Seconds 3; return $ob }, (Get-Process pwsh)); $j | Receive-Job NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName ------ ----- ----- ------ -- -- 0 0.00 559.55 94.22 53184 …84 pwsh 0 0.00 253.84 6.91 55195 …23 pwsh .EXAMPLE Wait-Task -ScriptBlock { Start-Sleep -Seconds 3; $input | Out-String } -InputObject (Get-Process pwsh) .EXAMPLE $RequestParams = @{ Uri = 'https://jsonplaceholder.typicode.com/todos/1' Method = 'GET' } $result = [ProgressUtil]::WaitJob("Making a request", { Param($rp) Start-Sleep -Seconds 2; Invoke-RestMethod @rp }, $RequestParams) | Receive-Job echo $result userId id title completed ------ -- ----- --------- 1 1 delectus aut autem False #> [Console]::CursorVisible = $false; [ValidateScript( { return [bool][color]$_ })][string]$PmsgColor = $PmsgColor [ProgressUtil]::data.TwirlFrames = [ProgressUtil]::data.TwirlEmojis[8]; $PBcolor = [ProgressUtil]::data.ProgressBarcolor [ValidateScript( { return [bool][color]$_ })][string]$PBcolor = $PBcolor [int]$length = [ProgressUtil]::data.TwirlFrames.Length; $originalY = [Console]::CursorTop while ($Job.JobStateInfo.State -notin ('Completed', 'failed')) { for ($i = 0; $i -lt $length; $i++) { [ProgressUtil]::data.TwirlFrames.Foreach({ Write-Console "$progressMsg" -NoNewLine -f $PmsgColor Write-Console " $($_[$i])" -NoNewLine -f $PBcolor }) [System.Threading.Thread]::Sleep(50) Write-Console ("`b" * ($length + $progressMsg.Length)) -NoNewLine -f $PmsgColor [Console]::CursorTop = $originalY } } Write-Console "`b$progressMsg ... " -NoNewLine -f $PmsgColor [System.Management.Automation.Runspaces.RemotingErrorRecord[]]$Errors = $Job.ChildJobs.Where({ $null -ne $_.Error } ).Error; if ($Job.JobStateInfo.State -eq "Failed" -or $Errors.Count -gt 0) { $errormessages = [string]::Empty if ($null -ne $Errors) { $errormessages = $Errors.Exception.Message -join "`n" } Write-Console "Completed with errors.`n`t$errormessages" -f Salmon } else { Write-Console "Done" -f Green } [Console]::CursorVisible = $true; return $Job } static [System.Management.Automation.Job] WaitJob([string]$progressMsg, [scriptblock]$sb, [Object[]]$ArgumentList) { $Job = ($null -ne $ArgumentList) ? (Start-ThreadJob -ScriptBlock $sb -ArgumentList $ArgumentList ) : (Start-ThreadJob -ScriptBlock $sb) return [ProgressUtil]::WaitJob($progressMsg, $Job) } static [void] ToggleShowProgress() { # .DESCRIPTION # The ShowProgress option respects $verbosepreference, this method enables you to take control of that and set/toggle it manualy. [ProgressUtil]::data.Set("ShowProgress", [scriptblock]::Create(" return [bool]$([int]![ProgressUtil]::data.ShowProgress)")) } } class StringTools { static [string[]] SplitLine([string]$String) { if ($String -notmatch "`n") { return , ([array]$String) } $ReturnValue = $String -split "`r`n" if ($ReturnValue.Count -eq 1) { $ReturnValue = $String -split "`n" } return $ReturnValue } static [Object[]] SplitStringOnLiteralString([string]$objToSplit, [string]$objSplitter) { if ([string]::IsNullOrEmpty($objToSplit)) { return @() } if ([string]::IsNullOrEmpty($objSplitter)) { return @($objToSplit) } $objSplitterInRegEx = [regex]::Escape($objSplitter) $result = @([regex]::Split($objToSplit, $objSplitterInRegEx)) return $result } static [string] ReverseString([string]$Inputstr) { if ([string]::IsNullOrEmpty($Inputstr)) { return $Inputstr } $charArray = $Inputstr.ToCharArray() [Array]::Reverse($charArray) return [string]::new($charArray) } static [string] ToTitleCase([string]$Inputstr) { if ([string]::IsNullOrEmpty($Inputstr)) { return $Inputstr } $TextInfo = (Get-Culture).TextInfo return $TextInfo.ToTitleCase($Inputstr.ToLower()) } } |