PSResourceGetProvider.ps1

function Get-PSResourceGetPackageProvider {
    [CmdletBinding()]
    param()

    [pscustomobject]@{
        Name             = 'PSResourceGet'
        Platforms        = @('Windows', 'Linux', 'MacOS')
        RequiredModules  = @('Microsoft.PowerShell.PSResourceGet', 'Shmuelie.Utilities')
        RequiredCommands = @(
            'Microsoft.PowerShell.PSResourceGet\Find-PSResource'
            'Microsoft.PowerShell.PSResourceGet\Save-PSResource'
            'Microsoft.PowerShell.PSResourceGet\Get-PSResourceRepository'
            'Shmuelie.Utilities\Update-InstalledPSResource'
        )
        OptionNames      = @('Path', 'Name', 'Exclude', 'Repository')
        TestAvailable    = {
            param([hashtable]$Options)
            $paths = @(Resolve-PSResourceGetPackagePath -Options $Options)
            if (-not $paths.Count) {
                return [pscustomobject]@{
                    Available = $false
                    Reason = 'Configure ProviderOptions.PSResourceGet.Path with one or more existing module root directories previously used with Save-PSResource.'
                }
            }
            $module = (Get-Command 'Shmuelie.Utilities\Update-InstalledPSResource' -ListImported -ErrorAction Stop).Module
            $compatible = & $module {
                foreach ($name in 'Get-InstalledPSResourceInPath', 'Compare-PSResourceVersion') {
                    if (-not (Get-Command $name -CommandType Function -ListImported -ErrorAction Ignore)) { return $false }
                }
                return $true
            }
            [pscustomobject]@{
                Available = $compatible
                Reason = if (-not $compatible) { 'Update Shmuelie.Utilities to a version providing the PSResource discovery and version-comparison helpers.' } else { $null }
            }
        }
        GetTargets       = {
            param([hashtable]$Options)
            foreach ($path in Resolve-PSResourceGetPackagePath -Options $Options) {
                foreach ($resource in Get-PSResourceGetPackageResource -Path $path -Name $Options.Name -Exclude $Options.Exclude) {
                    # The canonical Name selector splits commas, even in literal names.
                    if ($resource.Name.Contains(',')) {
                        throw "Module '$($resource.Name)' cannot be individually selected by Update-InstalledPSResource because its name contains a comma."
                    }
                    New-PackageUpdateTarget -Target (Join-Path $path $resource.Name) -PreviousVersion $resource.Version -Data @{
                        Path = $path
                        Resource = $resource
                    }
                }
            }
        }
        Update           = {
            param($Target, [hashtable]$Options)
            $parameters = @{
                Path = $Target.Data.Path
                Name = [System.Management.Automation.WildcardPattern]::Escape($Target.Data.Resource.Name)
                Confirm = $false
                ErrorAction = 'Stop'
                WarningVariable = 'providerWarnings'
                WarningAction = 'Continue'
            }
            if ($Options.ContainsKey('Repository')) { $parameters.Repository = $Options.Repository }
            $providerWarnings = @()
            # Relay warnings in this module's scope so aggregate warning preferences
            # and WarningVariable apply across the optional module boundary.
            Shmuelie.Utilities\Update-InstalledPSResource @parameters 3>&1 | ForEach-Object {
                if ($_ -is [System.Management.Automation.WarningRecord]) { Write-Warning $_.Message }
            }

            $observed = @(Get-PSResourceGetPackageResource -Path $parameters.Path -Name $parameters.Name)
            if ($observed.Count -ne 1) {
                throw "Could not observe the installed version of '$($Target.Target)' after Update-InstalledPSResource; the update outcome is unknown."
            }
            $module = (Get-Command 'Shmuelie.Utilities\Update-InstalledPSResource' -ListImported -ErrorAction Stop).Module
            $comparison = & $module {
                param($Left, $Right)
                Compare-PSResourceVersion -Left $Left -Right $Right
            } $observed[0] $Target.Data.Resource
            if ($comparison -lt 0) {
                throw "The installed version of '$($Target.Target)' decreased from '$($Target.PreviousVersion)' to '$($observed[0].Version)' during the update."
            }
            $status = if ($comparison -gt 0) { 'Updated' } elseif ($providerWarnings.Count) { 'Skipped' } else { 'Unchanged' }
            $reason = if ($providerWarnings.Count) {
                ($providerWarnings | ForEach-Object { $_.Message }) -join [Environment]::NewLine
            } elseif ($comparison -eq 0) {
                'No newer installed version was observed. The canonical command does not distinguish current modules from silent skips (for example, a module not found in its repository).'
            } else { $null }
            New-PackageUpdateResult -Provider PSResourceGet -Target $Target.Target -PreviousVersion $Target.PreviousVersion `
                -ResultingVersion $observed[0].Version -Status $status -Reason $reason
        }
    }
}

function Resolve-PSResourceGetPackagePath {
    [CmdletBinding()]
    param([Parameter(Mandatory)][hashtable]$Options)

    foreach ($name in 'Path', 'Name', 'Exclude') {
        if (-not $Options.ContainsKey($name)) { continue }
        $value = $Options[$name]
        if ($null -eq $value -or ($value -isnot [string] -and $value -isnot [array])) {
            throw "PSResourceGet option '$name' must be a string or an array of strings."
        }
        foreach ($entry in @($value)) {
            if ($entry -isnot [string] -or [string]::IsNullOrWhiteSpace($entry)) {
                throw "PSResourceGet option '$name' must contain only nonempty strings."
            }
        }
    }
    if ($Options.ContainsKey('Repository') -and
        ($Options.Repository -isnot [string] -or [string]::IsNullOrWhiteSpace($Options.Repository))) {
        throw "PSResourceGet option 'Repository' must be a nonempty string."
    }

    $comparer = if ($IsWindows) { [StringComparer]::OrdinalIgnoreCase } else { [StringComparer]::Ordinal }
    $seen = [System.Collections.Generic.HashSet[string]]::new($comparer)
    foreach ($path in $Options.Path) {
        if (-not (Test-Path -LiteralPath $path -PathType Container -ErrorAction Stop)) {
            Write-Warning "Configured PSResourceGet module root '$path' does not exist or is not a directory; skipping this root."
            continue
        }
        $resolved = Resolve-Path -LiteralPath $path -ErrorAction Stop
        if ($resolved.Provider.Name -ne 'FileSystem') {
            throw "PSResourceGet module root '$path' must be a filesystem directory."
        }
        $root = [IO.Path]::TrimEndingDirectorySeparator($resolved.ProviderPath)
        if ($seen.Add($root)) { $root }
    }
}

function Get-PSResourceGetPackageResource {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Path,
        [string[]]$Name,
        [string[]]$Exclude
    )

    # Keep layout, filtering, provenance, and prerelease rules owned by Utilities.
    $module = (Get-Command 'Shmuelie.Utilities\Update-InstalledPSResource' -ListImported -ErrorAction Stop).Module
    & $module {
        param($Path, $Name, $Exclude)
        Get-InstalledPSResourceInPath -Path $Path -Name $Name -Exclude $Exclude -ErrorAction Stop
    } $Path $Name $Exclude
}