PoshToolbox.psm1

#region:
#region: ./src/Exceptions/New_ActiveDirectoryObjectNotFoundException.ps1
function New_ActiveDirectoryObjectNotFoundException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException] $Message,
            'System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException',
            [System.Management.Automation.ErrorCategory]::ObjectNotFound,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_ActiveDirectoryOperationException.ps1
function New_ActiveDirectoryOperationException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.DirectoryServices.ActiveDirectory.ActiveDirectoryOperationException] $Message,
            'System.DirectoryServices.ActiveDirectory.ActiveDirectoryOperationException',
            [System.Management.Automation.ErrorCategory]::InvalidOperation,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_ActiveDirectoryServerDownException.ps1
function New_ActiveDirectoryServerDownException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.DirectoryServices.ActiveDirectory.ActiveDirectoryServerDownException] $Message,
            'System.DirectoryServices.ActiveDirectory.ActiveDirectoryServerDownException',
            [System.Management.Automation.ErrorCategory]::ResourceUnavailable,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_ArgumentException.ps1
function New_ArgumentException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.ArgumentException] $Message,
            'System.ArgumentException',
            [System.Management.Automation.ErrorCategory]::InvalidArgument,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_InvalidOperationException.ps1
function New_InvalidOperationException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.InvalidOperationException] $Message,
            'System.InvalidOperationException',
            [System.Management.Automation.ErrorCategory]::ConnectionError,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_LimitException.ps1
function New_LimitException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [PoshToolbox.LimitException] $Message,
            'PoshToolbox.LimitException',
            [System.Management.Automation.ErrorCategory]::LimitsExceeded,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_MethodInvocationException.ps1
function New_MethodInvocationException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [System.Exception] $Exception,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            $Exception,
            'System.Management.Automation.MethodInvocationException',
            [System.Management.Automation.ErrorCategory]::InvalidOperation,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_PSInvalidOperationException.ps1
function New_PSInvalidOperationException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.Management.Automation.PSInvalidOperationException] $Message,
            'System.Management.Automation.PSInvalidOperationException',
            [System.Management.Automation.ErrorCategory]::InvalidOperation,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Exceptions/New_UnauthorizedAccessException.ps1
function New_UnauthorizedAccessException {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw = $false
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.UnauthorizedAccessException] $Message,
            'System.UnauthorizedAccessException',
            [System.Management.Automation.ErrorCategory]::PermissionDenied,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#endregion
#region:
#region: ./src/Public/ConvertFrom-Base64String.ps1
function ConvertFrom-Base64String {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [OutputType([object])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            Position = 0
        )]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
                if ($_ -notmatch '^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$') {
                    throw 'The argument specified must be a valid base 64 string.'
                }
                return $true
            })]
        [string[]] $InputObject,

        [Parameter()]
        [switch] $AsString = $false
    )

    ## LOGIC ###################################################################
    process {
        foreach ($Object in $InputObject) {
            try {
                [System.Collections.Generic.Dictionary[string, System.IDisposable]] $Disposable = @{}

                $Disposable.MemoryWriter = [System.IO.MemoryStream]::new()

                [byte[]] $Buffer = [System.Console]::InputEncoding.GetBytes($Object)
                $Disposable.MemoryWriter.Write($Buffer, 0, $Buffer.Length)

                $Disposable.MemoryWriter.Flush()
                $Disposable.MemoryWriter.Position = 0

                $Disposable.CryptoStream = [System.Security.Cryptography.CryptoStream]::new(
                    $Disposable.MemoryWriter,
                    [System.Security.Cryptography.FromBase64Transform]::new(),
                    [System.Security.Cryptography.CryptoStreamMode]::Read
                )

                $Disposable.MemoryReader = [System.IO.MemoryStream]::new()
                $Disposable.CryptoStream.CopyTo($Disposable.MemoryReader)

                [byte[]] $Bytes = $Disposable.MemoryReader.ToArray()
                [string] $String = [System.Text.Encoding]::UTF8.GetString($Bytes)

                if ($AsString) {
                    $PSCmdlet.WriteObject($String)
                } else {
                    try {
                        $PSCmdlet.WriteObject([System.Management.Automation.PSSerializer]::Deserialize($String))
                    } catch {
                        $PSCmdlet.WriteObject($Bytes)
                    }
                }
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            } finally {
                $Disposable.Values.Dispose()
            }
        }
    }
}
#endregion
#region: ./src/Public/ConvertTo-Base64String.ps1
function ConvertTo-Base64String {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [OutputType([string])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            Position = 0
        )]
        [ValidateNotNullOrEmpty()]
        [object] $InputObject,

        [Parameter()]
        [int32] $Depth = 2
    )

    ## LOGIC ###################################################################
    process {
        try {
            [System.Collections.Generic.Dictionary[string, System.IDisposable]] $Disposable = @{}

            $Disposable.MemoryWriter = [System.IO.MemoryStream]::new()

            try {
                [byte[]] $Buffer = $InputObject
            } catch {
                if ($InputObject -is [string]) {
                    [byte[]] $Buffer = [System.Text.Encoding]::UTF8.GetBytes($InputObject)
                } else {
                    [xml] $Serialize = [System.Management.Automation.PSSerializer]::Serialize($InputObject, $Depth)

                    [byte[]] $Buffer = [System.Text.Encoding]::UTF8.GetBytes($Serialize.OuterXml)
                }
            }

            $Disposable.MemoryWriter.Write($Buffer, 0, $Buffer.Length)

            $Disposable.MemoryWriter.Flush()
            $Disposable.MemoryWriter.Position = 0

            $Disposable.CryptoStream = [System.Security.Cryptography.CryptoStream]::new(
                $Disposable.MemoryWriter,
                [System.Security.Cryptography.ToBase64Transform]::new(),
                [System.Security.Cryptography.CryptoStreamMode]::Read
            )

            $Disposable.MemoryReader = [System.IO.MemoryStream]::new()
            $Disposable.CryptoStream.CopyTo($Disposable.MemoryReader)

            [byte[]] $Bytes = $Disposable.MemoryReader.ToArray()
            [string] $String = [System.Console]::OutputEncoding.GetString($Bytes)

            $PSCmdlet.WriteObject($String)
        } catch [System.Management.Automation.MethodInvocationException] {
            $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
        } catch {
            $PSCmdlet.WriteError($_)
        } finally {
            $Disposable.Values.Dispose()
        }
    }
}
#endregion
#region: ./src/Public/Find-NlMtu.ps1
function Find-NlMtu {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSPossibleIncorrectUsageOfAssignmentOperator', '',
        Justification = 'Assignment Operator is intended.')]

    [CmdletBinding()]
    [OutputType([PoshToolbox.FindNlMtuCommand+NlMtuInfo])]

    param (
        [Alias('Hostname', 'IPAddress', 'Address')]
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            Position = 0
        )]
        [ValidateNotNullOrEmpty()]
        [string[]] $ComputerName,

        [Parameter()]
        [ValidateRange(1, [int32]::MaxValue)]
        [int32] $Timeout = 10000,

        [Alias('Ttl', 'TimeToLive', 'Hops')]
        [Parameter()]
        [ValidateRange(1, [int32]::MaxValue)]
        [int32] $MaxHops = 128
    )

    ## LOGIC ###################################################################
    begin {
        [System.Net.NetworkInformation.PingOptions] $PingOptions = @{
            'Ttl'          = $MaxHops
            'DontFragment' = $true
        }
    }

    process {
        foreach ($Computer in $ComputerName) {
            try {
                [System.Collections.Generic.Dictionary[string, System.IDisposable]] $Disposable = @{}

                $Disposable.Ping = [System.Net.NetworkInformation.Ping]::new()
                [uint16] $UpperBound = 65535
                [uint16] $LowerBound = 1

                [int32] $Size = 9000
                [byte[]] $Buffer = [byte[]]::new($Size)

                [System.Collections.Generic.List[System.Net.NetworkInformation.PingReply]] $Result = @()

                while ($Size -ne $LowerBound) {
                    try {
                        $PSCmdlet.WriteVerbose("PING ${Computer} with ${Size}-byte payload")

                        [System.Net.NetworkInformation.PingReply] $Reply = $Disposable.Ping.Send($Computer, $Timeout, $Buffer, $PingOptions)
                    } catch {
                        New_InvalidOperationException -Message "Connection to '${Computer}' failed." -Throw
                    }

                    switch ($Reply.Status) {
                        'PacketTooBig' { $UpperBound = $Size }
                        'Success' { $LowerBound = $Size }
                        'TimedOut' { $UpperBound = $Size }
                        default {
                            New_InvalidOperationException -Message "Connection to '${Computer}' failed with status '$( $Reply.Status ).'" -Throw
                        }
                    }

                    $Result.Add($Reply)

                    if (($Size = [System.Math]::Floor(($LowerBound + $UpperBound) / 2)) -eq 1) {
                        New_InvalidOperationException -Message "Connection to '${Computer}' failed with status 'NoReply.'" -Throw
                    }

                    [array]::Resize([ref] $Buffer, $Size)
                }

                if (([int32] $Hops = $MaxHops - $Result.Where{ $_.Status -eq 'Success' }[-1].Options.Ttl) -lt 0) {
                    $Hops = 0
                }

                $PSCmdlet.WriteObject(
                    [PoshToolbox.FindNlMtuCommand+NlMtuInfo]::new(
                        $Computer,
                        $Result.Where{ $_.Status -eq 'Success' }[-1].Address,
                        [int64] ($Result.Where{ $_.Status -eq 'Success' }.RoundtripTime | Measure-Object -Average).Average,
                        $Hops,
                        $Size + 28 # IP Header (20 bytes) + ICMP Header (8 bytes) = 28 bytes
                    )
                )
            } catch {
                $PSCmdlet.WriteError($_)
            } finally {
                $Disposable.Values.Dispose()
            }
        }
    }
}
#endregion
#region: ./src/Public/Get-ADServiceAccountCredential.ps1
function Get-ADServiceAccountCredential {
    # Copyright (c) 2021 Ryan Ephgrave, https://github.com/Ryan2065/gMSACredentialModule, MIT License
    # Modified "GMSACredential/Commands/Get-GMSACredential.ps1" by Anthony J. Raymond
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '',
        Justification = 'Retrieved password is plaintext.')]

    [CmdletBinding()]
    [OutputType([pscredential])]

    param (
        [Alias('distinguishedName', 'objectGuid', 'objectSid', 'sAMAccountName')]
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            Position = 0
        )]
        [ValidateNotNullOrEmpty()]
        [string[]] $Identity,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Server
    )

    ## LOGIC ###################################################################
    begin {
        [hashtable[]] $Properties = @(
            @{ n = 'sAMAccountName'; e = { $_.Properties.'samaccountname' } }
            @{ n = 'Length'; e = { $_.Properties.'msds-managedpassword'.Length } }
            @{ n = 'ManagedPassword'; e = {
                    [int32] $Length = $_.Properties.'msds-managedpassword'.Length
                    [int32] $IntPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Length)

                    [System.Runtime.InteropServices.Marshal]::Copy(
                        [byte[]] $_.Properties.'msds-managedpassword'.ForEach{ $_ },
                        0,
                        $IntPtr,
                        $Length
                    )

                    return $IntPtr
                }
            }
        )
    }

    process {
        foreach ($Object in $Identity) {
            try {
                [System.Collections.Generic.Dictionary[string, System.IDisposable]] $Disposable = @{}

                try {
                    # https://ldapwiki.com/wiki/ObjectGuid
                    [string] $ObjectGuid = ([guid] $Object).ToByteArray().ForEach{ $_.ToString('X2') } -join '\'
                    [string] $Filter = "(&(objectGuid=\${ObjectGuid})(ObjectCategory=msDS-GroupManagedServiceAccount))"
                } catch {
                    [string] $Filter = '(&(|(distinguishedName={0})(objectSid={0})(sAMAccountName={1}))(ObjectCategory=msDS-GroupManagedServiceAccount))' -f $Object, ($Object -ireplace '[^$]$', '$&$')
                }

                $Disposable.DirectorySearcher = [System.DirectoryServices.DirectorySearcher]::new($Filter)
                if ($Server) { $Disposable.DirectorySearcher.SearchRoot = "LDAP://${Server}" }

                $Disposable.DirectorySearcher.SearchRoot.AuthenticationType = 'Sealing'
                $Disposable.DirectorySearcher.PropertiesToLoad.AddRange(@('sAMAccountName', 'msDS-ManagedPassword'))

                [object] $ADServiceAccount = $Disposable.DirectorySearcher.FindOne() | Select-Object -Property $Properties

                if (-not $ADServiceAccount) {
                    New_ActiveDirectoryObjectNotFoundException -Message "Cannot find an object with identity: '${Object}' under: '$( $Disposable.DirectorySearcher.SearchRoot.distinguishedName )'." -Throw
                } elseif ($ADServiceAccount.Length -eq 0) {
                    New_ActiveDirectoryOperationException -Message 'Cannot retrieve service account password. A process has requested access to an object, but has not been granted those access rights.' -Throw
                }

                # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/a9019740-3d73-46ef-a9ae-3ea8eb86ac2e
                [securestring] $SecureString = ConvertTo-SecureString -String ([System.Runtime.InteropServices.Marshal]::PtrToStringUni([int64] $ADServiceAccount.ManagedPassword + 16)) -AsPlainText -Force

                $PSCmdlet.WriteObject([pscredential]::new($ADServiceAccount.sAMAccountName, $SecureString))
            } catch [System.Management.Automation.SetValueInvocationException] {
                $PSCmdlet.WriteError(( New_ActiveDirectoryServerDownException -Message 'Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Services running.' ))
            } catch {
                $PSCmdlet.WriteError($_)
            } finally {
                $Disposable.Values.Dispose()

                if ($ADServiceAccount) {
                    [System.Runtime.InteropServices.Marshal]::Copy(
                        [byte[]]::new($ADServiceAccount.Length),
                        0,
                        $ADServiceAccount.ManagedPassword,
                        $ADServiceAccount.Length
                    )
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ADServiceAccount.ManagedPassword)

                    $ADServiceAccount = $SecureString = $null
                    $null = [System.GC]::GetTotalMemory($true)
                }
            }
        }
    }
}
#endregion
#region: ./src/Public/Get-FolderProperties.ps1
function Get-FolderProperties {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '',
        Justification = 'Named to match Windows context menu.')]

    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([PoshToolbox.GetFolderPropertiesCommand+FolderPropertiesInfo])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            Position = 0,
            ParameterSetName = 'Path'
        )]
        [ValidateScript({
                if (Test-Path -Path $_ -PathType 'Container') {
                    return $true
                }
                throw 'The argument specified must resolve to a valid folder path.'
            })]
        [PoshToolbox.FileSystemPathTransformation()]
        [System.IO.DirectoryInfo[]] $Path,

        [Alias('PSPath')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'LiteralPath'
        )]
        [ValidateScript({
                if (Test-Path -LiteralPath $_ -PathType 'Container') {
                    return $true
                }
                throw 'The argument specified must resolve to a valid folder path.'
            })]
        [PoshToolbox.FileSystemLiteralPathTransformation()]
        [System.IO.DirectoryInfo[]] $LiteralPath,

        [Parameter()]
        [ValidateSet(
            'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', # Decimal Metric (Base 10)
            'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' # Binary IEC (Base 2)
        )]
        [string] $Unit
    )

    ## LOGIC ###################################################################
    begin {
        [string[]] $Suffixes = @(
            'B' # byte
            'KB'   # kilo
            'MB'   # mega
            'GB'   # giga
            'TB'   # tera
            'PB'   # peta
            'EB'   # exa
            'ZB'   # zetta
            'YB'   # yotta
        )
    }

    process {
        foreach ($Object in $PSBoundParameters[$PSCmdlet.ParameterSetName]) {
            try {
                $PSCmdlet.WriteVerbose("GET ${Object}")

                [int32] $Dirs = [int32] $Files = [int64] $Bytes = 0
                # https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
                [string[]] $Result = robocopy $Object.FullName.TrimEnd('/\') \\null /l /e /np /xj /r:0 /w:0 /bytes /nfl /ndl

                if (($LASTEXITCODE -eq 16) -and ($Result[-2] -eq 'Access is denied.')) {
                    New_UnauthorizedAccessException -Message "Access to the path '${Object}' is denied." -Throw
                } elseif ($LASTEXITCODE -eq 16) {
                    New_ArgumentException -Message "The specified path '${Object}' is invalid." -Throw
                }

                switch -Regex ($Result) {
                    'Dirs :\s+(?<match>\d+)' { $Dirs = $Matches.match - 1 }
                    'Files :\s+(?<match>\d+)' { $Files = $Matches.match }
                    'Bytes :\s+(?<match>\d+)' { $Bytes = $Matches.match }
                }

                # Copyright (c) 2021 Santiago Squarzon, https://github.com/santisq/PSTree, MIT License
                # Modified "src/PSTree/Internal/_FormattingInternals.cs" by Anthony J. Raymond
                [double] $Size = $Bytes
                [int32] $Base = if ($Unit -match 'i') { 1024 } else { 1000 }
                [int32] $Index = 0
                [int32] $StopIndex = $Suffixes.IndexOf($Unit -replace 'i')

                while ((($StopIndex -eq -1) -and ($Size -ge $Base)) -or (($StopIndex -ne -1) -and ($Index -lt $StopIndex))) {
                    $Size /= $Base

                    $Index++
                }

                [string] $Suffix = if ($Unit) { $Unit } else { $Suffixes[$Index] }

                $PSCmdlet.WriteObject(
                    [PoshToolbox.GetFolderPropertiesCommand+FolderPropertiesInfo]::new(
                        $Object.FullName,
                        $Bytes,
                        "$( $Size.ToString('N2') ) ${Suffix}",
                        "${Files} Files, ${Dirs} Folders",
                        $Object.CreationTime
                    )
                )
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
#endregion
#region: ./src/Public/Invoke-ExponentialBackoff.ps1
function Invoke-ExponentialBackoff {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [OutputType([object])]

    param (
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [scriptblock] $ScriptBlock,

        [Parameter()]
        [int32] $RetryCount = 3,

        [Parameter()]
        [int32] $Base = 2,

        [Parameter()]
        [int32] $Scalar = 1
    )

    ## LOGIC ###################################################################
    end {
        for ([int32] $i = 0; $i -lt $RetryCount; $i++) {
            try {
                . $ScriptBlock

                return
            } catch {
                $PSCmdlet.WriteError($_)

                if (($i + 1) -ge $RetryCount) {
                    $PSCmdlet.ThrowTerminatingError(( New_LimitException -Message "The operation has reached the limit of ${RetryCount} retries." ))
                }

                Start-Sleep -Milliseconds ((Get-Random -Maximum 1000) * $Scalar * [bigint]::Pow($Base, $i))
            }
        }
    }
}
#endregion
#region: ./src/Public/Join-File.ps1
function Join-File {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding(
        DefaultParameterSetName = 'Path',
        SupportsShouldProcess
    )]
    [OutputType([System.IO.FileInfo])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            Position = 0,
            ParameterSetName = 'Path'
        )]
        [ValidateScript({
                if (Test-Path -Path $_ -PathType 'Leaf' -Filter '*.*split') {
                    return $true
                }
                throw 'The argument specified must resolve to a valid split type file.'
            })]
        [PoshToolbox.FileSystemPathTransformation()]
        [System.IO.FileInfo[]] $Path,

        [Alias('PSPath')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'LiteralPath'
        )]
        [ValidateScript({
                if (Test-Path -LiteralPath $_ -PathType 'Leaf' -Filter '*.*split') {
                    return $true
                }
                throw 'The argument specified must resolve to a valid split type file.'
            })]
        [PoshToolbox.FileSystemLiteralPathTransformation()]
        [System.IO.FileInfo[]] $LiteralPath,

        [Parameter()]
        [PoshToolbox.ReturnFirstOrInputTransformation()]
        [PoshToolbox.FileSystemLiteralPathTransformation()]
        [System.IO.DirectoryInfo] $Destination = (Get-Location -PSProvider 'FileSystem').Path
    )

    ## LOGIC ###################################################################
    process {
        foreach ($Object in $PSBoundParameters[$PSCmdlet.ParameterSetName]) {
            try {
                [System.Collections.Generic.Dictionary[string, System.IDisposable]] $Disposable = @{}

                [string] $CalculatedDestination = if ($Destination.Extension) { $Destination } else { "$( $Destination.FullName.TrimEnd('/\') )/$( $Object.BaseName )" }

                if ($PSCmdlet.ShouldProcess($CalculatedDestination, 'Write Content')) {
                    [string] $Directory = if ($Destination.Extension) { $Destination.Parent } else { $Destination }

                    if (-not $Directory.Exists) { $null = [System.IO.Directory]::CreateDirectory($Directory) }

                    $PSCmdlet.WriteVerbose("WRITE ${CalculatedDestination}")

                    $Disposable.Writer = [System.IO.File]::OpenWrite($CalculatedDestination)
                    # sort to fix ChildItem number sorting
                    foreach ($SplitFile in (Get-ChildItem -Path "$( $Object.Directory )/$( $Object.BaseName ).*split").FullName | Sort-Object -Property { [int32] [regex]::Match($_, '\.(?<match>\d+)split$').Groups['match'].Value }) {
                        $PSCmdlet.WriteVerbose("READ ${SplitFile}")

                        [byte[]] $Bytes = [System.IO.File]::ReadAllBytes($SplitFile)
                        $Disposable.Writer.Write($Bytes, 0, $Bytes.Length)
                    }
                }

                $PSCmdlet.WriteObject(( Get-ChildItem -Path $CalculatedDestination ))
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            } finally {
                $Disposable.Values.Dispose()
            }
        }
    }
}
#endregion
#region: ./src/Public/New-Exception.ps1
function New-Exception {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]

    param(
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [ValidateNotNullOrEmpty()]
        [string] $Message,

        [Parameter()]
        [switch] $Throw
    )

    ## LOGIC ###################################################################
    end {
        [System.Management.Automation.ErrorRecord] $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.Exception] $Message,
            'System.Exception',
            [System.Management.Automation.ErrorCategory]::NotSpecified,
            $null
        )

        if ($Throw) {
            throw $ErrorRecord
        }

        $PSCmdlet.WriteObject($ErrorRecord)
    }
}
#endregion
#region: ./src/Public/New-IPAddress.ps1
function New-IPAddress {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Net.IPAddress])]

    param (
        [Alias('Address')]
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            Position = 0
        )]
        [string[]] $IPAddress
    )

    ## LOGIC ###################################################################
    process {
        foreach ($Address in $IPAddress) {
            try {
                $PSCmdlet.WriteObject([System.Net.IPAddress]::Parse($Address))
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
#endregion
#region: ./src/Public/New-IPSubnet.ps1
function New-IPSubnet {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Function does not change system state.')]

    [CmdletBinding()]
    [OutputType([System.Net.IPSubnet])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            Position = 0,
            ParameterSetName = 'InputObject'
        )]
        [string[]] $InputObject,

        [Alias('Address')]
        [Parameter(
            Mandatory,
            ParameterSetName = 'IPAddress'
        )]
        [string] $IPAddress,

        [Alias('Prefix')]
        [Parameter(
            Mandatory,
            ParameterSetName = 'IPAddress'
        )]
        [int32] $IPPrefix
    )

    ## LOGIC ###################################################################
    process {
        foreach ($Object in $PSBoundParameters[$PSCmdlet.ParameterSetName]) {
            try {
                if ($PSCmdlet.ParameterSetName -eq 'InputObject') {
                    $IPAddress = ($Object -isplit '\\|\/')[0]
                    $IPPrefix = ($Object -isplit '\\|\/')[-1]
                }

                $PSCmdlet.WriteObject([System.Net.IPSubnet]::Parse($IPAddress, $IPPrefix))
            } catch [System.Management.Automation.MethodException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
#endregion
#region: ./src/Public/Resolve-PoshPath.ps1
function Resolve-PoshPath {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([PoshToolbox.ResolvePoshPathCommand+PoshPathInfo])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            Position = 0,
            ParameterSetName = 'Path'
        )]
        [ValidateNotNullOrEmpty()]
        [string[]] $Path,

        [Alias('PSPath')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'LiteralPath'
        )]
        [ValidateNotNullOrEmpty()]
        [string[]] $LiteralPath
    )

    ## LOGIC ###################################################################
    process {
        foreach ($PSPath in $PSBoundParameters[$PSCmdlet.ParameterSetName]) {
            try {
                if ($PSCmdlet.ParameterSetName -eq 'Path') {
                    try {
                        [string[]] $PSPath = $PSCmdlet.SessionState.Path.GetResolvedPSPathFromPSPath($PSPath)
                    } catch [System.Management.Automation.MethodInvocationException] {
                        [string[]] $PSPath = $_.Exception.InnerException.ItemName
                    }
                }

                foreach ($String in $PSPath) {
                    [System.Management.Automation.ProviderInfo] $Provider = $null
                    [System.Management.Automation.PSDriveInfo] $Drive = $null

                    [string] $ProviderPath = $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($String, [ref] $Provider, [ref] $Drive)

                    $PSCmdlet.WriteObject(
                        [PoshToolbox.ResolvePoshPathCommand+PoshPathInfo]::new(
                            $String,
                            $ProviderPath,
                            $Provider,
                            $Drive
                        )
                    )
                }
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
#endregion
#region: ./src/Public/Split-File.ps1
function Split-File {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding(
        DefaultParameterSetName = 'Path',
        SupportsShouldProcess
    )]
    [OutputType([System.IO.FileInfo])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            Position = 0,
            ParameterSetName = 'Path'
        )]
        [ValidateScript({
                if (Test-Path -Path $_ -PathType 'Leaf') {
                    return $true
                }
                throw 'The argument specified must resolve to a valid file path.'
            })]
        [PoshToolbox.FileSystemPathTransformation()]
        [System.IO.FileInfo[]] $Path,

        [Alias('PSPath')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'LiteralPath'
        )]
        [ValidateScript({
                if (Test-Path -LiteralPath $_ -PathType 'Leaf') {
                    return $true
                }
                throw 'The argument specified must resolve to a valid file path.'
            })]
        [PoshToolbox.FileSystemLiteralPathTransformation()]
        [System.IO.FileInfo[]] $LiteralPath,

        [Parameter()]
        [PoshToolbox.ReturnFirstOrInputTransformation()]
        [PoshToolbox.FileSystemLiteralPathTransformation()]
        [System.IO.DirectoryInfo] $Destination = (Get-Location -PSProvider 'FileSystem').Path,

        [Parameter (
            Mandatory
        )]
        [ValidateRange(0, [int32]::MaxValue)]
        [int32] $Size
    )

    ## LOGIC ###################################################################
    process {
        foreach ($Object in $PSBoundParameters[$PSCmdlet.ParameterSetName]) {
            try {
                [System.Collections.Generic.Dictionary[string, System.IDisposable]] $Disposable = @{}

                $PSCmdlet.WriteVerbose("READ ${Object}")

                $Disposable.Reader = [System.IO.File]::OpenRead($Object)
                [byte[]] $Buffer = [byte[]]::new($Size)
                [int32] $Count = 1

                [string] $CalculatedDestination = if ($Destination.Extension) { "$( $Destination.Parent.FullName )/$( $Object.Name )" } else { "$( $Destination.FullName.TrimEnd('/\') )/$( $Object.Name )" }

                while ([int32] $Read = $Disposable.Reader.Read($Buffer, 0, $Buffer.Length)) {
                    if ($Read -ne $Buffer.Length) {
                        [array]::Resize([ref] $Buffer, $Read)
                    }

                    [string] $SplitFile = "${CalculatedDestination}.${Count}split"
                    if ($PSCmdlet.ShouldProcess($SplitFile, 'Write Content')) {
                        [string] $Directory = if ($Destination.Extension) { $Destination.Parent } else { $Destination }

                        if (-not $Directory.Exists) { $null = [System.IO.Directory]::CreateDirectory($Directory) }

                        $PSCmdlet.WriteVerbose("WRITE ${SplitFile}")

                        [System.IO.File]::WriteAllBytes($SplitFile, $Buffer)
                    }

                    $Count++
                }

                # sort to fix ChildItem number sorting
                $PSCmdlet.WriteObject(( Get-ChildItem -Path "${CalculatedDestination}.*split" | Sort-Object -Property { [int32] [regex]::Match($_.FullName, '\.(?<match>\d+)split$').Groups['match'].Value } ))
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            } finally {
                $Disposable.Values.Dispose()
            }
        }
    }
}
#endregion
#region: ./src/Public/Start-PoshLog.ps1
function Start-PoshLog {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Justification = 'As designed to receive log events.')]
    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([void])]
    param (
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'Path'
        )]
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'PathAppend'
        )]
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'PathNoClobber'
        )]
        [ValidateScript({
                if (Test-Path -Path $_ -IsValid) {
                    return $true
                }
                throw 'The argument specified must resolve to a valid file or folder path.'
            })]
        [string[]]
        $Path = [Environment]::GetFolderPath('MyDocuments'),

        [Alias('PSPath')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'LiteralPath'
        )]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'LiteralPathAppend'
        )]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'LiteralPathNoClobber'
        )]
        [ValidateScript({
                if (Test-Path -LiteralPath $_ -IsValid) {
                    return $true
                }
                throw 'The argument specified must resolve to a valid file or folder path.'
            })]
        [string[]]
        $LiteralPath,

        [Parameter(ParameterSetName = 'PathAppend')]
        [Parameter(ParameterSetName = 'LiteralPathAppend')]
        [switch]
        $Append,

        [Parameter(ParameterSetName = 'PathNoClobber')]
        [Parameter(ParameterSetName = 'LiteralPathNoClobber')]
        [switch]
        $NoClobber,

        [Parameter()]
        [switch]
        $AsUtc,

        [Parameter()]
        [switch]
        $PassThru
    )

    ## LOGIC ###################################################################
    begin {
        $DateTime = [datetime]::Now

        $Format = $AsUtc | Use-Ternary ('yyyy\-MM\-dd HH:mm:ss\Z', 'ToUniversalTime', 'yyyyMMdd\-HHmmss\Z') ('yyyy\-MM\-dd HH:mm:ss', 'ToLocalTime', 'yyyyMMdd\-HHmmss')
        $FileMode = $Append | Use-Ternary { [System.IO.FileMode]::Append } { $NoClobber | Use-Ternary { [System.IO.FileMode]::CreateNew } { [System.IO.FileMode]::Create } }

        $Template = {
            '**********************'
            'Windows PowerShell log start'
            'Version: {0} ({1})' -f $PSVersionTable.PSVersion, $PSVersionTable.PSEdition
            "Start time: {0:$( $Format[0] )}" -f $DateTime.($Format[1]).Invoke()
            '**********************'
        }
    }

    process {
        $Process = ($PSCmdlet.ParameterSetName -cmatch '^LiteralPath') | Use-Ternary { Resolve-PoshPath -LiteralPath $LiteralPath } { Resolve-PoshPath -Path $Path }

        foreach ($Object in $Process) {
            try {
                if ($Object.Provider.Name -ne 'FileSystem') {
                    New_ArgumentException 'The argument specified must resolve to a valid path on the FileSystem provider.' -Throw
                }

                $FileInfo = [System.IO.FileInfo] $Object.ProviderPath

                if (-not ($Directory = $FileInfo.Extension | Use-Ternary { $FileInfo.Directory } { $FileInfo }).Exists) {
                    $null = [System.IO.Directory]::CreateDirectory($Directory)
                }

                if (-not $FileInfo.Extension) {
                    $FileInfo = [System.IO.FileInfo] ("PowerShell_log.{0}.{1:$( $Format[2] )}.txt" -f ([guid]::NewGuid() -isplit '-')[0], $DateTime.($Format[1]).Invoke())
                }

                Use-Object ($File = [System.IO.File]::Open($Directory.FullName + '\' + $FileInfo.Name, $FileMode)) {
                    if ($Append) {
                        $NewLine = [System.Text.Encoding]::UTF8.GetBytes([System.Environment]::NewLine)
                        $File.Write($NewLine, 0, $NewLine.Length)
                    }

                    foreach ($Line in $Template.Invoke()) {
                        $Bytes = [System.Text.Encoding]::UTF8.GetBytes($Line + [System.Environment]::NewLine)
                        $File.Write($Bytes, 0, $Bytes.Length)
                    }
                }

                $Global:PSLogDetails += @(@{ Path = $File.Name; Utc = $AsUtc })
                Write-Information -InformationAction Continue -MessageData ("Log started, output file is '{0}'" -f $File.Name) -InformationVariable null

                ## EXCEPTIONS ##################################################
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }

    end {
        if (-not (Get-EventSubscriber | Where-Object SourceIdentifier -CMatch '^PSLog') -and $PSLogDetails) {
            $Global:PSLogInformation = [System.Collections.ObjectModel.ObservableCollection[System.Management.Automation.InformationRecord]]::new()
            $Global:PSLogWarning = [System.Collections.ObjectModel.ObservableCollection[System.Management.Automation.WarningRecord]]::new()
            $Global:PSLogError = [System.Collections.ObjectModel.ObservableCollection[System.Management.Automation.ErrorRecord]]::new()

            $Action = { Write-PoshLog -PSEventArgs $Event }

            $null = Register-ObjectEvent -EventName CollectionChanged -InputObject $PSLogInformation -SourceIdentifier PSLogInformation -Action $Action
            $null = Register-ObjectEvent -EventName CollectionChanged -InputObject $PSLogWarning -SourceIdentifier PSLogWarning -Action $Action
            $null = Register-ObjectEvent -EventName CollectionChanged -InputObject $PSLogError -SourceIdentifier PSLogError -Action $Action

            $Global:PSDefaultParameterValues['Write-Information:InformationVariable'] = '+PSLogInformation'
            $Global:PSDefaultParameterValues['Write-Warning:WarningVariable'] = '+PSLogWarning'
            $Global:PSDefaultParameterValues['Write-Error:ErrorVariable'] = '+PSLogError'
        }

        if ($PassThru) {
            Write-Output (, $PSLogDetails.Path)
        }
    }
}
#endregion
#region: ./src/Public/Stop-PoshLog.ps1
function Stop-PoshLog {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [OutputType([void])]
    param ()

    ## LOGIC ###################################################################
    end {
        if (-not $PSLogDetails) {
            $PSCmdlet.ThrowTerminatingError(( New_PSInvalidOperationException -Message 'An error occurred stopping the log: The host is not currently logging.' ))
        }

        if ($Events = Get-EventSubscriber | Where-Object SourceIdentifier -CMatch '^PSLog') {
            $Events | Unregister-Event

            $Global:PSDefaultParameterValues.Remove('Write-Information:InformationVariable')
            $Global:PSDefaultParameterValues.Remove('Write-Warning:WarningVariable')
            $Global:PSDefaultParameterValues.Remove('Write-Error:ErrorVariable')
        }

        $DateTime = [datetime]::Now

        $Template = {
            '**********************'
            'Windows PowerShell log end'
            "End time: {0:$( $Format[0] )}" -f $DateTime.($Format[1]).Invoke()
            '**********************'
        }

        foreach ($PSLog in $PSLogDetails) {
            try {
                $Format = $PSLog.Utc | Use-Ternary ('yyyy\-MM\-dd HH:mm:ss\Z', 'ToUniversalTime') ('yyyy\-MM\-dd HH:mm:ss', 'ToLocalTime')

                Use-Object ($File = [System.IO.File]::AppendText($PSLog.Path)) {
                    foreach ($Line in $Template.Invoke()) {
                        $File.WriteLine($Line)
                    }
                }

                Write-Information -InformationAction Continue -MessageData ("Log stopped, output file is '{0}'" -f $PSLog.Path) -InformationVariable null

                ## EXCEPTIONS ##################################################
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }

        Remove-Variable -Name PSLog* -Scope Global
    }
}
#endregion
#region: ./src/Public/Use-ErrorCoalescing.ps1
function Use-ErrorCoalescing {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [Alias('?!')]
    [OutputType([object])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [scriptblock] $InputObject,

        [Parameter(
            Position = 0
        )]
        [AllowNull()]
        [object] $IfError
    )

    ## LOGIC ###################################################################
    process {
        foreach ($Object in , $InputObject) {
            try {
                . $Object
            } catch {
                [System.Management.Automation.ErrorRecord] $ErrorRecord = $_

                [object] $Output = if ($IfError -is [System.Collections.IDictionary]) {
                    $IfError.GetEnumerator().Where{ $ErrorRecord.Exception -is $_.Key }[0].Value
                } else {
                    $IfError
                }

                if ($Output -is [scriptblock]) {
                    $Output.InvokeWithContext(
                        $null,
                        [psvariable]::new('_', $ErrorRecord),
                        $null
                    )
                } else {
                    $PSCmdlet.WriteObject($Output)
                }
            }
        }
    }
}
#endregion
#region: ./src/Public/Use-NullCoalescing.ps1
function Use-NullCoalescing {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [Alias('??')]
    [OutputType([object])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [AllowNull()]
        [AllowEmptyString()]
        [AllowEmptyCollection()]
        [object] $InputObject,

        [Parameter(
            Mandatory,
            Position = 0
        )]
        [object] $IfNull
    )

    ## LOGIC ###################################################################
    end {
        if (-not ($InputObject = $input)) {
            $InputObject = , $null
        }

        foreach ($Object in $InputObject) {
            try {
                if ($null -eq $Object) {
                    if ($IfNull -is [scriptblock]) {
                        . $IfNull
                    } else {
                        $PSCmdlet.WriteObject($IfNull)
                    }
                } else {
                    $PSCmdlet.WriteObject($Object)
                }
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
#endregion
#region: ./src/Public/Use-Object.ps1
function Use-Object {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [OutputType([object])]

    param (
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [AllowEmptyString()]
        [AllowEmptyCollection()]
        [AllowNull()]
        [object] $InputObject,

        [Parameter(
            Mandatory,
            Position = 1
        )]
        [scriptblock] $ScriptBlock
    )

    ## LOGIC ###################################################################
    end {
        try {
            $ScriptBlock.InvokeWithContext(
                $null,
                [psvariable]::new('_', $InputObject),
                $null
            )
        } catch {
            throw $_
        } finally {
            foreach ($Object in $InputObject) {
                if ($Object -is [System.IDisposable]) {
                    $Object.Dispose()
                } elseif ([System.Runtime.InteropServices.Marshal]::IsComObject($Object)) {
                    $null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Object)
                }
            }
        }
    }
}
#endregion
#region: ./src/Public/Use-Ternary.ps1
function Use-Ternary {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding()]
    [Alias('?:')]
    [OutputType([object])]

    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [AllowNull()]
        [AllowEmptyString()]
        [AllowEmptyCollection()]
        [object] $InputObject,

        [Parameter(
            Position = 0
        )]
        [object] $IfTrue,

        [Parameter(
            Position = 1
        )]
        [object] $IfFalse
    )

    ## LOGIC ###################################################################
    process {
        foreach ($Object in , $InputObject) {
            try {
                [object] $Output = if ($Object) { $IfTrue } else { $IfFalse }

                if ($Output -is [scriptblock]) {
                    . $Output
                } else {
                    $PSCmdlet.WriteObject($Output)
                }
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
#endregion
#region: ./src/Public/Write-PoshLog.ps1
function Write-PoshLog {
    # Copyright (c) 2023 Anthony J. Raymond, MIT License (see manifest for details)
    [CmdletBinding(
        DefaultParameterSetName = 'Type'
    )]
    [OutputType([void])]
    param (
        [Parameter(
            Mandatory,
            DontShow,
            ParameterSetName = 'PSEventArgs'
        )]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSEventArgs]
        $PSEventArgs,

        [Parameter(
            ParameterSetName = 'Type'
        )]
        [ValidateSet('Log', 'Information', 'Warning', 'Error')]
        [string]
        $Type = 'Log',

        [Parameter(
            Mandatory,
            Position = 0,
            ParameterSetName = 'Type'
        )]
        [string]
        $Message
    )

    ## LOGIC ###################################################################
    end {
        if (-not $PSLogDetails) {
            $PSCmdlet.ThrowTerminatingError(( New_PSInvalidOperationException -Message 'An error occurred writing the log: The host is not currently logging.' ))
        }

        $TypeMap = @{
            'Log'         = 'LOG'
            'Information' = 'INFO'
            'Warning'     = 'WARN'
            'Error'       = 'ERROR'
        }

        $Template = {
            "{0:$( $Format[0] )}`t[{1}] `t{2}" -f $DateTime.($Format[1]).Invoke(), $TypeMap.$Type, $Message
        }

        if ($PSEventArgs) {
            $DateTime = $PSEventArgs.TimeGenerated

            switch ($PSEventArgs.SourceIdentifier) {
                'PSLogInformation' {
                    $Type = 'Information'
                    $Message = $PSEventArgs.SourceEventArgs.NewItems.MessageData
                }
                'PSLogWarning' {
                    $Type = 'Warning'
                    $Message = $PSEventArgs.SourceEventArgs.NewItems.Message
                }
                'PSLogError' {
                    $Type = 'Error'
                    $Message = $PSEventArgs.SourceEventArgs.NewItems.Exception.Message
                }
            }
        } else {
            $DateTime = [datetime]::Now
        }

        foreach ($PSLog in $PSLogDetails) {
            try {
                $Format = $PSLog.Utc | Use-Ternary ('yyyy\-MM\-dd HH:mm:ss\Z', 'ToUniversalTime') ('yyyy\-MM\-dd HH:mm:ss', 'ToLocalTime')

                Use-Object ($File = [System.IO.File]::AppendText($PSLog.Path)) {
                    $File.WriteLine($Template.Invoke()[0])
                }

                ## EXCEPTIONS ##################################################
            } catch [System.Management.Automation.MethodInvocationException] {
                $PSCmdlet.WriteError(( New_MethodInvocationException -Exception $_.Exception.InnerException ))
            } catch {
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
#endregion
#endregion