Classes/PSCumulus.PathModel.ps1
|
enum CloudPathDepth { Root Scope Kind Resource } class CloudPath { [string]$Provider [string]$Scope [string]$Kind [string]$ResourceName [CloudPathDepth]$Depth CloudPath() {} [string] ToString() { switch ($this.Depth) { 'Root' { return "$($this.Provider):\" } 'Scope' { return "$($this.Provider):\$($this.Scope)" } 'Kind' { return "$($this.Provider):\$($this.Scope)\$($this.Kind)" } 'Resource' { return "$($this.Provider):\$($this.Scope)\$($this.Kind)\$($this.ResourceName)" } } return '' } static [CloudPath] Parse([string]$path) { if ([string]::IsNullOrWhiteSpace($path)) { throw [System.ArgumentException]::new('Path cannot be null or empty.', 'path') } $result = [CloudPath]::new() if (-not $path.Contains(':')) { throw [System.ArgumentException]::new("Path must contain a provider separator ':'. Got: '$path'", 'path') } $providerPart, $remainder = $path -split ':', 2 $providerName = $providerPart.Trim() $validProviders = @('Azure', 'AWS', 'GCP') $matchedProvider = $validProviders | Where-Object { $_ -eq $providerName } | Select-Object -First 1 if (-not $matchedProvider) { throw [System.ArgumentException]::new("Invalid provider '$providerName'. Must be one of: Azure, AWS, GCP.", 'path') } $result.Provider = $matchedProvider if ([string]::IsNullOrWhiteSpace($remainder)) { $result.Depth = [CloudPathDepth]::Root return $result } $remainder = $remainder.Trim() if ($remainder.StartsWith('\')) { $remainder = $remainder.Substring(1) } if ($remainder.EndsWith('\')) { $remainder = $remainder.Substring(0, $remainder.Length - 1) } if ([string]::IsNullOrWhiteSpace($remainder)) { $result.Depth = [CloudPathDepth]::Root return $result } $segments = $remainder -split '\\' if ($segments.Count -ge 1) { $result.Scope = $segments[0] $result.Depth = [CloudPathDepth]::Scope } if ($segments.Count -ge 2) { $kindSegment = $segments[1] $validKinds = [System.Collections.Generic.Dictionary[string,string]]::new([StringComparer]::OrdinalIgnoreCase) $validKinds['Instance'] = 'Instances' $validKinds['Instances'] = 'Instances' $validKinds['Disk'] = 'Disks' $validKinds['Disks'] = 'Disks' $validKinds['Storage'] = 'Storage' $validKinds['Network'] = 'Networks' $validKinds['Networks'] = 'Networks' $validKinds['Function'] = 'Functions' $validKinds['Functions'] = 'Functions' $validKinds['Tag'] = 'Tags' $validKinds['Tags'] = 'Tags' $canonicalKind = if ($validKinds.ContainsKey($kindSegment)) { $validKinds[$kindSegment] } else { throw [System.ArgumentException]::new("Invalid kind '$kindSegment'. Must be one of: Instances, Disks, Storage, Networks, Functions, Tags.", 'path') } $result.Kind = $canonicalKind $result.Depth = [CloudPathDepth]::Kind } if ($segments.Count -ge 3) { $result.ResourceName = $segments[2] $result.Depth = [CloudPathDepth]::Resource } if ($segments.Count -gt 3) { throw [System.ArgumentException]::new("Path has too many segments. Maximum 3 segments after provider. Got: $($segments.Count)", 'path') } return $result } static [bool] IsValid([string]$path) { try { $null = [CloudPath]::Parse($path) return $true } catch { return $false } } } class CloudPathResolver { static [hashtable] Resolve([CloudPath]$cloudPath) { if ($null -eq $cloudPath) { throw [System.ArgumentNullException]::new('cloudPath') } $result = @{ CommandName = $null ArgumentMap = @{} } $backendCommand = [CloudPathResolver]::GetBackendCommand($cloudPath.Provider, $cloudPath.Kind) $result.CommandName = $backendCommand $scopeArgs = [CloudPathResolver]::GetScopeArgument($cloudPath.Provider, $cloudPath.Scope) foreach ($key in $scopeArgs.Keys) { $result.ArgumentMap[$key] = $scopeArgs[$key] } if ($cloudPath.Depth -eq [CloudPathDepth]::Resource) { switch ($cloudPath.Provider) { 'Azure' { $result.ArgumentMap['Name'] = $cloudPath.ResourceName } 'AWS' { $result.ArgumentMap['Name'] = $cloudPath.ResourceName } 'GCP' { $result.ArgumentMap['Name'] = $cloudPath.ResourceName } } } return $result } static [string] GetBackendCommand([string]$provider, [string]$kind) { $kindToCommand = @{ 'Instances' = 'Instance' 'Disks' = 'Disk' 'Storage' = 'Storage' 'Networks' = 'Network' 'Functions' = 'Function' 'Tags' = 'Tag' } $commandKind = if ($kind -and $kindToCommand.ContainsKey($kind)) { $kindToCommand[$kind] } else { throw [System.ArgumentException]::new("Invalid or missing kind '$kind'.") } return "Get-$provider${commandKind}Data" } static [hashtable] GetScopeArgument([string]$provider, [string]$scope) { $result = switch ($provider) { 'Azure' { @{ ResourceGroup = $scope } } 'AWS' { @{ Region = $scope } } 'GCP' { @{ Project = $scope } } default { throw [System.ArgumentException]::new("Invalid provider '$provider'.") } } return $result } } |