functions/utility/Resolve-PSFItem.ps1
function Resolve-PSFItem { <# .SYNOPSIS Resolves paths provided. .DESCRIPTION Resolves paths provided. This command is designed as the ultimate tool for resolving paths provided with all flow control decisions handled. The key difference between this command and Resolve-PSFPath is that this command directly integrates into the caller for the purposes of error handling. It also handles a lot of the flow control issues merging and refining input and informing about issues. Resolve-PSFPath simply takes a path and resolves that one path. .PARAMETER Path The paths to resolve. Interprets wildcards. .PARAMETER LiteralPath The paths to resolve. Does not interpret wildcards. .PARAMETER Type What kind of item to return: - Any: Return anything of the correct provider. - File/Leaf: Only return file (or leaf) objects. - Directory/Container: Only return directory (or container) objects. Default: Any .PARAMETER ResolutionMode The resolution mode determines in which situation the command figures there is an actual error. ErrorMode then determines how bad of an error to generate. - Any: Any number of results (including none) is ok. In this scenario we do not generate an error. - All: For each path provided, at least one item must be found. Any input path without result causes an error. - AtLeastOne: At least one path must have been found in total - OnlyOne: More than one result in total causes an error. Default: Any .PARAMETER WarningMode Warnings are potentially generated for each path that has no result. Warning processing is independent of error handling. - None: No warning is generated, no matter what - One: One summary warning is generated, listing all input paths without a result - All: One warning is generated for each path without results Default: One .PARAMETER ErrorMode If the ResolutionMode has determined, that an error state exists, it is up to this ErrorMode parameter to determine just what kind of error state happens. - Terminating: A terminating error is generated - NonTerminating: A non-terminating error is generated Default: Terminating .PARAMETER ProviderName Name of the provider generating the items. Defaults to: FileSystem .PARAMETER Cmdlet The $PSCmdlet object representing the calling command If this parameter is specified, the error is executed in the context of the calling command, not Resolve-PSFItem. .EXAMPLE PS C:\> Resolve-PSFItem -Path $Path -LiteralPath $LiteralPath -Cmdlet $PSCmdlet Searches all items found under the specified paths. .EXAMPLE PS C:\> Resolve-PSFItem -Path $Path -Type File -ResolutionMode AtLeastOne -WarningMode None -Cmdlet $PSCmdlet Searches for all files under $Path. No warning will ever be generated, but at least one file must be found, otherwise the calling command is killed with a terminating exception. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "")] [CmdletBinding()] param ( [AllowNull()] [AllowEmptyCollection()] [string[]] $Path, [AllowNull()] [AllowEmptyCollection()] [string[]] $LiteralPath, [ValidateSet('Any', 'File', 'Directory', 'Leaf', 'Container')] [string] $Type = 'Any', [ValidateSet('Any', 'All', 'AtLeastOne', 'OnlyOne')] [string] $ResolutionMode = 'Any', [ValidateSet('None', 'One', 'All')] [string] $WarningMode = 'One', [ValidateSet('Terminating', 'NonTerminating')] [string] $ErrorMode = 'Terminating', [PsfArgumentCompleter('PSFramework-utility-psprovider')] [string] $ProviderName = 'FileSystem', $Cmdlet ) begin { #region Filters $filters = @{ 'Any' = { $_.PSProvider.Name -eq $ProviderName } 'File' = { $_.PSProvider.Name -eq $ProviderName -and -not $_.PSIsContainer } 'Leaf' = { $_.PSProvider.Name -eq $ProviderName -and -not $_.PSIsContainer } 'Directory' = { $_.PSProvider.Name -eq $ProviderName -and $_.PSIsContainer } 'Container' = { $_.PSProvider.Name -eq $ProviderName -and $_.PSIsContainer } } #endregion Filters } process { #region Process Paths $badPaths = [System.Collections.ArrayList]@() $foundItems = [System.Collections.ArrayList]@() foreach ($entry in $Path) { $results = Get-Item -Path $entry -ErrorAction Ignore | Where-Object $filters[$Type] Write-PSFMessage -Level Debug -String 'Resolve-PSFItem.Path.Found' -StringValues $entry, @($results).Count -Target $entry if ($results) { $null = $foundItems.AddRange(@($results)) } else { $null = $badPaths.Add($entry) } } foreach ($entry in $LiteralPath) { $results = Get-Item -LiteralPath $entry -ErrorAction Ignore | Where-Object $filters[$Type] Write-PSFMessage -Level Debug -String 'Resolve-PSFItem.Path.Found' -StringValues $entry, @($results).Count -Target $entry if ($results) { $null = $foundItems.AddRange(@($results)) } else { $null = $badPaths.Add($entry) } } Write-PSFMessage -Level Debug -String 'Resolve-PSFItem.Path.Summary' -StringValues (@($Path).Count + @($LiteralPath).Count), $foundItems.Count, $badPaths.Count #endregion Process Paths #region Process Warnings if ($badPaths) { switch ($WarningMode) { 'One' { Write-PSFMessage -Level Warning -String 'Resolve-PSFItem.BadPaths' -StringValues ($badPaths -join ', ') } 'All' { foreach ($badPath in $badPaths) { Write-PSFMessage -Level Warning -String 'Resolve-PSFItem.BadPath' -StringValues $badPath -Target $badPath } } } } #endregion Process Warnings #region Process Error State $mustDie = $false switch ($ResolutionMode) { 'All' { if ($badPaths) { $mustDie = $true } } 'AtLeastOne' { if (-not $foundItems) { $mustDie = $true } } 'OnlyOne' { if (@($foundItems).Count -ne 1) { $mustDie = $true } } } $myCmdlet = $Cmdlet if (-not $myCmdlet) { $myCmdlet = $PSCmdlet } if ($mustDie) { $category = [System.Management.Automation.ErrorCategory]::InvalidArgument switch ($ErrorMode) { 'Terminating' { $target = $badPaths.ToArray() $message = switch ($ResolutionMode) { 'All' { 'Not all paths could be resolved. Bad input: {0}' -f ($badPaths -join "," ) } 'AtLeastOne' { 'No item could be found under any specified path' } 'OnlyOne' { if (-not $foundItems) { 'No item could be found under any specified path' } else { 'Must resolve to a single item. {0} items found!' -f @($foundItems).Count } } } $record = [PSFramework.Meta.PsfErrorRecord]::new($message, $category, 'InvalidArgument', $target) $myCmdlet.ThrowTerminatingError($record) } 'NonTerminating' { if ($ResolutionMode -eq 'OnlyOne' -and @($foundItems).Count -gt 1) { $goodPaths1 = $Path | Where-Object { $_ -notin $badPaths } $goodPaths2 = $LiteralPath | Where-Object { $_ -notin $badPaths } $message = 'Must resolve to a single item. {0} items found!' -f @($foundItems).Count $record = [PSFramework.Meta.PsfErrorRecord]::new($message, $category, 'InvalidArgument', (@($goodPaths1) + @($goodPaths2))) $myCmdlet.WriteError($record) break } foreach ($entry in $badPaths) { $message = 'No item found under {0}' -f $entry $record = [PSFramework.Meta.PsfErrorRecord]::new($message, $category, 'InvalidArgument', $entry) $myCmdlet.WriteError($record) } } } } #endregion Process Error State $foundItems } } |